perm filename LRNSAM.DGL[UP,DOC] blob sn#348663 filedate 1978-04-19 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00116 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00018 00002
C00020 00003
C00022 00004	LRNSAM                                                                     DRAFT
C00026 00005	LRNSAM                         Table of Contents                           DRAFT
C00031 00006	LRNSAM                         Table of Contents                           DRAFT
C00037 00007	LRNSAM                         Table of Contents                           DRAFT
C00041 00008	LRNSAM                                                                     DRAFT
C00045 00009	LRNSAM                                                                     DRAFT
C00050 00010	LRNSAM                                                                     DRAFT
C00055 00011	LRNSAM                 Processing ticks and update ticks                   DRAFT
C00060 00012	LRNSAM                                                                     DRAFT
C00064 00013	LRNSAM                       Tick time requirements                        DRAFT
C00067 00014	LRNSAM                                                                     DRAFT
C00072 00015	LRNSAM                   Processing speed vs. capacity                     DRAFT
C00077 00016	LRNSAM                   Processing speed vs. capacity                     DRAFT
C00080 00017	LRNSAM                                                                     DRAFT
C00084 00018	LRNSAM                      The processing elements                        DRAFT
C00086 00019	LRNSAM                                                                     DRAFT
C00087 00020	LRNSAM                                                                     DRAFT
C00091 00021	LRNSAM                          The flow of data                           DRAFT
C00095 00022	LRNSAM                          The flow of data                           DRAFT
C00097 00023	LRNSAM                                                                     DRAFT
C00102 00024	LRNSAM                             Sum memory                              DRAFT
C00107 00025	LRNSAM                             Sum memory                              DRAFT
C00109 00026	LRNSAM                                                                     DRAFT
C00114 00027	LRNSAM                             Generators                              DRAFT
C00118 00028	LRNSAM                             Generators                              DRAFT
C00121 00029	LRNSAM                                                                     DRAFT
C00125 00030	LRNSAM                        Generator run modes                          DRAFT
C00130 00031	LRNSAM                        Generator run modes                          DRAFT
C00135 00032	LRNSAM                        Generator run modes                          DRAFT
C00136 00033	LRNSAM                                                                     DRAFT
C00137 00034	LRNSAM                                                                     DRAFT
C00143 00035	LRNSAM                       Oscillator processing                         DRAFT
C00145 00036	LRNSAM                                                                     DRAFT
C00149 00037	LRNSAM                          Oscillator modes                           DRAFT
C00153 00038	LRNSAM                          Oscillator modes                           DRAFT
C00155 00039	LRNSAM                                                                     DRAFT
C00160 00040	LRNSAM                        Envelope processing                          DRAFT
C00163 00041	LRNSAM                                                                     DRAFT
C00167 00042	LRNSAM                           Envelope modes                            DRAFT
C00169 00043	LRNSAM                                                                     DRAFT
C00173 00044	LRNSAM                     Shape-shifting generators                       DRAFT
C00178 00045	LRNSAM                     Shape-shifting generators                       DRAFT
C00182 00046	LRNSAM                                                                     DRAFT
C00187 00047	LRNSAM                           Magic numbers                             DRAFT
C00193 00048	LRNSAM                           Magic numbers                             DRAFT
C00196 00049	LRNSAM                                                                     DRAFT
C00200 00050	LRNSAM                             Modifiers                               DRAFT
C00205 00051	LRNSAM                             Modifiers                               DRAFT
C00207 00052	LRNSAM                                                                     DRAFT
C00211 00053	LRNSAM                        Modifier procedures                          DRAFT
C00215 00054	LRNSAM                        Modifier procedures                          DRAFT
C00218 00055	LRNSAM                        Modifier procedures                          DRAFT
C00222 00056	LRNSAM                        Modifier procedures                          DRAFT
C00227 00057	LRNSAM                        Modifier procedures                          DRAFT
C00231 00058	LRNSAM                        Modifier procedures                          DRAFT
C00232 00059	LRNSAM                                                                     DRAFT
C00235 00060	LRNSAM                        Filtering algorithms                         DRAFT
C00237 00061	LRNSAM                        Filtering algorithms                         DRAFT
C00241 00062	LRNSAM                        Filtering algorithms                         DRAFT
C00246 00063	LRNSAM                        Filtering algorithms                         DRAFT
C00251 00064	LRNSAM                        Filtering algorithms                         DRAFT
C00252 00065	LRNSAM                        Filtering algorithms                         DRAFT
C00253 00066	LRNSAM                        Filtering algorithms                         DRAFT
C00254 00067	LRNSAM                                                                     DRAFT
C00255 00068	LRNSAM                                                                     DRAFT
C00259 00069	LRNSAM                            Delay Units                              DRAFT
C00263 00070	LRNSAM                            Delay Units                              DRAFT
C00265 00071	LRNSAM                                                                     DRAFT
C00269 00072	LRNSAM                          Calling the box                            DRAFT
C00273 00073	LRNSAM                          Calling the box                            DRAFT
C00277 00074	LRNSAM                          Calling the box                            DRAFT
C00281 00075	LRNSAM                          Calling the box                            DRAFT
C00284 00076	LRNSAM                          Calling the box                            DRAFT
C00286 00077	LRNSAM                          Calling the box                            DRAFT
C00290 00078	LRNSAM                          Calling the box                            DRAFT
C00293 00079	LRNSAM                          Calling the box                            DRAFT
C00296 00080	LRNSAM                          Calling the box                            DRAFT
C00299 00081	LRNSAM                          Calling the box                            DRAFT
C00302 00082	LRNSAM                                                                     DRAFT
C00307 00083	LRNSAM                   An introduction to pipelining                     DRAFT
C00313 00084	LRNSAM                                                                     DRAFT
C00317 00085	LRNSAM                     Time division multiplexing                      DRAFT
C00321 00086	LRNSAM                                                                     DRAFT
C00325 00087	LRNSAM                        Basic number theory                          DRAFT
C00329 00088	LRNSAM                        Basic number theory                          DRAFT
C00332 00089	LRNSAM                        Basic number theory                          DRAFT
C00335 00090	LRNSAM                                                                     DRAFT
C00339 00091	LRNSAM                           SAIL examples                             DRAFT
C00343 00092	LRNSAM                           SAIL examples                             DRAFT
C00347 00093	LRNSAM                           SAIL examples                             DRAFT
C00351 00094	LRNSAM                           SAIL examples                             DRAFT
C00355 00095	LRNSAM                           SAIL examples                             DRAFT
C00359 00096	LRNSAM                           SAIL examples                             DRAFT
C00363 00097	LRNSAM                           SAIL examples                             DRAFT
C00367 00098	LRNSAM                           SAIL examples                             DRAFT
C00371 00099	LRNSAM                           SAIL examples                             DRAFT
C00375 00100	LRNSAM                           SAIL examples                             DRAFT
C00378 00101	LRNSAM                           SAIL examples                             DRAFT
C00382 00102	LRNSAM                           SAIL examples                             DRAFT
C00384 00103	LRNSAM                                                                     DRAFT
C00388 00104	LRNSAM                  Generator field and mode tables                    DRAFT
C00390 00105	LRNSAM                                                                     DRAFT
C00393 00106	LRNSAM                   Modifier field and mode tables                    DRAFT
C00396 00107	LRNSAM                                                                     DRAFT
C00399 00108	LRNSAM                                                                     DRAFT
C00403 00109	LRNSAM                           Reserved Words                            DRAFT
C00407 00110	LRNSAM                           Reserved Words                            DRAFT
C00411 00111	LRNSAM                           Reserved Words                            DRAFT
C00414 00112	LRNSAM                                                                     DRAFT
C00416 00113	LRNSAM                               Index                                 DRAFT
C00422 00114	LRNSAM                               Index                                 DRAFT
C00428 00115	LRNSAM                               Index                                 DRAFT
C00434 00116	
C00435 ENDMK
C⊗;












                                Systems Concepts
                              Digital Synthesizer
                               operations manual
                                  and tutorial



                                 by Gareth Loy
              Center for Computer Research in Music and Acoustics
                              Department of Music
                              Stanford University
                           Stanford, California 94305











This work was supported in part  by a grant from the National Endowment  for the
Arts and by NSF contract DCR 75-00694.



Abstract


This  document  describes  the  low level  operation  of  Pete  Samson's Systems
Concepts  Digital  Synthesizer,  beginning  with  a  general  discussion  of the
internal  and external  data  structures, then  the workings  of  the processing
elements, finally,  a discussion of  the SAIL procedures  which form  the lowest
level of user  control.  At the end  are appendices containing  introductions to
pipelining and  time-division multiplexing, and  examples of sample  calls using
the SAIL procedures.


Acknowledgments


The existence  of this document  is entirely due  to the patience,  teaching and
editing I have received over the course of its writing from Andy Moorer and Mark
Kahrs.
LRNSAM                                                                     DRAFT


                       T A B L E   O F   C O N T E N T S



                                    SECTION                                 PAGE



Section 1   Introduction
             1-1     The nature of the problem  .  .  .  .  .  .  .  .  .  .   1
             1-2     Reading this manual  .  .  .  .  .  .  .  .  .  .  .  .   1

Section 2   Definitions

Section 3   Processing ticks and update ticks

Section 4   Tick time requirements
             4-1     Generator tick time  .  .  .  .  .  .  .  .  .  .  .  .   5
             4-2     Modifier tick time   .  .  .  .  .  .  .  .  .  .  .  .   5
             4-3     Combined tick times  .  .  .  .  .  .  .  .  .  .  .  .   5
             4-4     General formula   .  .  .  .  .  .  .  .  .  .  .  .  .   6

Section 5   Processing speed vs. capacity
             5-1     Speed of processing  .  .  .  .  .  .  .  .  .  .  .  .   7
             5-2     Ticks per pass .  .  .  .  .  .  .  .  .  .  .  .  .  .   7
             5-3     Processing vs. update ticks   .  .  .  .  .  .  .  .  .   8
             5-4     Budgeting ticks   .  .  .  .  .  .  .  .  .  .  .  .  .   8
             5-5     Numbering Processing elements .  .  .  .  .  .  .  .  .   9

Section 6   The processing elements
             6-1     Generators  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  10
             6-2     Modifiers   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  10
             6-3     Sum memory  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  11
             6-4     Delay memory   .  .  .  .  .  .  .  .  .  .  .  .  .  .  11

Section 7   The flow of data
             7-1     The Connection... .  .  .  .  .  .  .  .  .  .  .  .  .  13
             7-2     Command path   .  .  .  .  .  .  .  .  .  .  .  .  .  .  13
             7-3     I/O path .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  14
             7-4     Read path and Write path   .  .  .  .  .  .  .  .  .  .  14
             7-5     DAC path .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  14
             7-6     It's a Stream machine...   .  .  .  .  .  .  .  .  .  .  14

Section 8   Sum memory
             8-1     This pass - last pass division   .  .  .  .  .  .  .  .  16
             8-2     Generator - modifier division .  .  .  .  .  .  .  .  .  17
             8-3     Addressing sum memory   .  .  .  .  .  .  .  .  .  .  .  17


                                     Page i
LRNSAM                         Table of Contents                           DRAFT


Section 9   Generators
             9-1     Generator parameters .  .  .  .  .  .  .  .  .  .  .  .  20

Section 10  Generator run modes
             10-1    INACTIVE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  22
             10-2    G_PAUSE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  22
             10-3    G_WAIT   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  23
             10-4    x_RUNNING   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  23
             10-5    DAC_WRITE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  24
             10-6    READ_DATA   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  24
             10-7    WRITE_DATA  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  24

Section 11  Oscillator processing

Section 12  Oscillator modes
             12-1    SINE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  29
             12-2    SAWTOOTH .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  29
             12-3    SQUARE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-4    PULSE_TRAIN .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-5    COS_FM   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-6    SUM_OF_COSINES .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-7    A note on band limiting .  .  .  .  .  .  .  .  .  .  .  31

Section 13  Envelope processing
             13-1    EXPONENT and RATE .  .  .  .  .  .  .  .  .  .  .  .  .  32
             13-2    ASYMPTOTE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  32

Section 14  Envelope modes

Section 15  Shape-shifting generators
             15-1    SWEEP .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  36
             15-2    FREQUENCY   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  37
             15-3    ANGLE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  37
             15-4    NCOSINES and SCALE   .  .  .  .  .  .  .  .  .  .  .  .  37
             15-5    FM .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  38
             15-6    SUM_MEMORY  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  38
             15-7    RATE EXPONENT and ASYMPTOTE   .  .  .  .  .  .  .  .  .  38

Section 16  Magic numbers

Section 17  Modifiers
             17-1    Modifier parameters  .  .  .  .  .  .  .  .  .  .  .  .  42
             17-2    Running terms  .  .  .  .  .  .  .  .  .  .  .  .  .  .  42
             17-3    Coefficient terms and Read terms .  .  .  .  .  .  .  .  43
             17-4    Scaling terms  .  .  .  .  .  .  .  .  .  .  .  .  .  .  43
             17-5    Mode  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  43
             17-6    Sum memory  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  44
             17-7    Initializing   .  .  .  .  .  .  .  .  .  .  .  .  .  .  44

                                    Page ii
LRNSAM                         Table of Contents                           DRAFT


Section 18  Modifier procedures
             18-1    Inactive .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  45
             18-2    Mixing   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  45
             18-3    Integer mixing .  .  .  .  .  .  .  .  .  .  .  .  .  .  46
             18-4    Latch .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  46
             18-5    Signum   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  47
             18-6    Zero-crossing pulser .  .  .  .  .  .  .  .  .  .  .  .  47
             18-7    Minimum  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  47
             18-8    Maximum  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  47
             18-9    Amplitude modulation .  .  .  .  .  .  .  .  .  .  .  .  48
             18-10   Four-quadrant-multiply  .  .  .  .  .  .  .  .  .  .  .  48
             18-11   Uniform noise  .  .  .  .  .  .  .  .  .  .  .  .  .  .  48
             18-12   Triggered uniform noise .  .  .  .  .  .  .  .  .  .  .  49
             18-13   Threshold   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  50
             18-14   Invoke delay unit .  .  .  .  .  .  .  .  .  .  .  .  .  50

Section 19  Filtering algorithms
             19-1    One pole .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  52
             19-2    One zero .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  52
             19-3    Two poles   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  53
             19-4    Two zeros   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  53
             19-5    Two poles COEFF0 variable  .  .  .  .  .  .  .  .  .  .  53
             19-6    Two poles COEFF1 variable  .  .  .  .  .  .  .  .  .  .  53
             19-7    Two zeros COEFF0 variable  .  .  .  .  .  .  .  .  .  .  54
             19-8    Two zeros COEFF1 variable  .  .  .  .  .  .  .  .  .  .  54
             19-9    A little digital filtering theory   .  .  .  .  .  .  .  54

Section 20  Delay Units
             20-1    Delay line mode   .  .  .  .  .  .  .  .  .  .  .  .  .  61
             20-2    Table lookup mode .  .  .  .  .  .  .  .  .  .  .  .  .  62
             20-3    Table lookup - rounded  .  .  .  .  .  .  .  .  .  .  .  62

Section 21  Calling the box
             21-1    GET   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  64
             21-2    GIVE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  65
             21-3    BIND  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  65
             21-4    SET_OUTPUT  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  66
             21-5    SET_CHANNEL .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  66
             21-6    SET_PROCEDURE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  67
             21-7    DECODE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  68
             21-8    RELATIVE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  68
             21-9    SET_MODE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  68
             21-10   SET_FIELD   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  69
             21-11   BIND_FIELD  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  70
             21-12   Size of command buffer  .  .  .  .  .  .  .  .  .  .  .  71
             21-13   LOAD_DELAY  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  71
             21-14   INITIALIZE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  71
             21-15   FLUSH .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  72

                                    Page iii
LRNSAM                         Table of Contents                           DRAFT


             21-16   Processing element arrays  .  .  .  .  .  .  .  .  .  .  72
             21-17   Tick counters  .  .  .  .  .  .  .  .  .  .  .  .  .  .  73
             21-18   Steps in calling the box   .  .  .  .  .  .  .  .  .  .  73

Appendix 1  An introduction to pipelining .  .  .  .  .  .  .  .  .  .  .  .  75

Appendix 2  Time division multiplexing .  .  .  .  .  .  .  .  .  .  .  .  .  77

Appendix 3  Basic number theory  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  79

Appendix 4  SAIL examples  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  83
             4-1     Generator example .  .  .  .  .  .  .  .  .  .  .  .  .  83
             4-2     Modifier example  .  .  .  .  .  .  .  .  .  .  .  .  .  87
             4-3     Delay line example   .  .  .  .  .  .  .  .  .  .  .  .  93
             4-4     Writing your own procedures   .  .  .  .  .  .  .  .  .  94

Appendix 5  Generator field and mode tables  .  .  .  .  .  .  .  .  .  .  .  96
             5-1     Generator parameters .  .  .  .  .  .  .  .  .  .  .  .  96
             5-2     Generator run modes  .  .  .  .  .  .  .  .  .  .  .  .  96
             5-3     Oscillator modes  .  .  .  .  .  .  .  .  .  .  .  .  .  97
             5-4     Envelope modes .  .  .  .  .  .  .  .  .  .  .  .  .  .  97

Appendix 6  Modifier field and mode tables   .  .  .  .  .  .  .  .  .  .  .  98
             6-1     Modifier parameters  .  .  .  .  .  .  .  .  .  .  .  .  98
             6-2     Modifier procedures  .  .  .  .  .  .  .  .  .  .  .  .  99

Appendix 7  Delay unit field and mode tables .  .  .  .  .  .  .  .  .  .  . 100
             7-1     Delay units: timing  .  .  .  .  .  .  .  .  .  .  .  . 100

Appendix 8  Reserved Words .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 101

Appendix 9  Error messages .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 105

















                                    Page iv
LRNSAM                                                                     DRAFT


                                   Section 1
                                   _______ _

                                  Introduction
                                  ____________





1-1. The nature of the problem

The Samson  box creates sounds  by producing streams  of binary  numbers, called
samples, at a  blinding rate of speed.   The process includes digital  to analog
conversion which changes these samples into analog voltages that  are eventually
fed to a  sound system.  There  is a need for  extreme speed in  calculating the
samples which can be appreciated if it is realized that according to the Nyquist
Sampling Theorem, one cannot represent  a frequency higher than 1/2 of  the rate
at which the samples are  produced (called the sampling rate).  This  means that
for reasonably good hi-fi sound, where the frequency response might go up  to 15
kHz or beyond, the  machine must be capable of  at least a 30 kHz  sampling rate
per channel of sound.  This can be easy or difficult to do depending on how much
computation goes  into each sample.   But typically, each  sample is made  up of
great amounts of computation such that the speed limits of most modern computers
can be quickly surpassed, especially considering that production  of interesting
music by  computers typically  requires a  great deal  of sophistication  in the
models used to generate the sounds.  So we see that we must be clever in  how we
structure the computations in order to  get a good fast sampling rate  but still
to have enough time left over to compute each sample.  The Samson box is a study
in cleverness.



1-2. Reading this manual

This manual  starts out assuming  an elementary knowlege  of the  basic computer
science concepts of pipelining  and time division multiplexing.  If  these terms
are unfamiliar to you you may want  to first read appendicies 1 and 2  which are
designed to  impart an understanding  of these things  to the (not  quite total)
novice user.   If you  would like to  review basic  digital number  theory, read
appendix 3.

The later sections  on calling the  Samson box from  a SAIL environment  and the
subsequent program examples  are designed to be  read by those  already familiar
with the  SAIL language.   Generally, if you  are only  interested in  the basic
processing of the box read until section 21.





                                     Page 1
LRNSAM                                                                     DRAFT


                                   Section 2
                                   _______ _

                                  Definitions
                                  ___________



Before proceeding let's define some terms.  These definitions are  elaborated in
later sections of this manual.

The Samson box  has two basic active  parts, or processing elements.   The first
element is called  a generator, (see  section 9) the  second is a  modifier (see
section 17).  There are 256 generators and 128 modifiers.

The processing elements communicate  with each other through a  scratchpad read-
alter memory called sum memory which is used to store the output from, and input
to the processing elements.  There are 256 locations in sum memory.  See section
8.

Delay  units  are a  special  kind of  processing  element.  They  must  work in
conjunction with modifiers.   They can reference  up to 65k  of 20 bit  words of
memory (of which Stanford  has 48k words). It  is called delay memory.   This is
for reverberation and general table lookups. See section 20.

The amount of time that it takes for the box to produce one cycle of output from
all the running modifiers and generators is called a pass.  A pass is made up of
a series of ticks.  A tick is the smallest counting unit in the box.  It  is the
amount  of  time  necessary  for  the  slowest  micro-process  to  complete  its
operation.  There are two types of ticks.

Processing  ticks count  the computations  in time  of the  processing elements.
Update ticks count the number of  new values passed to the box from  the outside
world.  Both kinds of ticks take the same amount of time.  In other  words, when
sound samples are being computed  we are counting in processing ticks.   When we
are updating the box with new  parameters supplied by the outside world,  we are
using update ticks.  The sum of all processing ticks and all update ticks equals
a pass.  See section 3.

A pass  is not  to be  confused with a  sample.  A  sample is  the result  of an
individual sound pressure wave calculation.  The number of samples produced in a
pass equals the  number of channels outputing  sounds.  If the box  is producing
one channel of music, then there will be one sample of sound produced  per pass.
There are  16 possible  DAC (digital  to analog  converter) addresses,  of which
Stanford has eight.






                                     Page 2
LRNSAM                                                                     DRAFT


                                   Section 3
                                   _______ _

                       Processing ticks and update ticks
                       __________ _____ ___ ______ _____



Now let's take an  example that will show how  the two kinds of  ticks interact.
Since a pass is the sum of processing and update ticks, we must first anticipate
the maximum number of processing  element parameters we will want to  change per
pass.  This is the  number of update ticks  required. It takes one  tick-time to
update  one parameter  in a  processing element.   Then we  figure out  how many
processing ticks it  will take to  allow all the  procesing elements we  want to
run.  Each  running processing  element takes  9 tick-times.   The sum  of these
numbers is the total time in ticks  the box will take to complete one  pass.  We
foreward the total number of ticks  thus calculated to the box on a  command and
the length of a pass is  set accordingly.  When the box runs, it  first executes
all the processing  ticks, then the  update ticks are run.   So when the  box is
cranked up, it first enters the processing mode and it will act on  the existing
numbers in the  processing elements, then it  enters update mode and  will allow
one variable inside one processing element to be changed on each succeeding tick
until the end of the pass.

Please  note the  that update  ticks have  the lowest  priority.  If  we  end up
needing to  update more  parameters on one  pass than  we have  update tick-time
available then the remainder will not  happen on this pass, but will  be snarfed
according to  how many update  ticks there  are and you'll  probably not  get an
error message.  If it  affects the sound, as  it undoubtedly will, it  is called
signal degradation.  That may or may not be reasonable depending on  what you're
doing.  The  obvious thing to  do is  to make a  compromise between  the minimum
necessary update  ticks and  the longest  acceptable pass  period to  produce an
acceptable sampling rate such  as will allow plenty  of time for all  the update
ticks you can estimate needing.

Lets take as an example  a generator that has two user-settable  locations.  One
location contains the number that represents the frequency.  The  other location
contains a number which changes  the number in the frequency location  by adding
its value to the number in the frequency location on every pass, thereby raising
or lowering the frequency uniformly by some amount or "slewing" it,  producing a
glissando.  We will diagram it this way:










                                     Page 3
LRNSAM                 Processing ticks and update ticks                   DRAFT


                                SLEW
                                 ↓
                                 + ←←←←←←
                                 ↓       ↑
                              FREQUENCY  ↑
                                 ↓       ↑
                                 ↓ →→→→→→
                                 ↓
                              GENERATOR
                                 ↓
                               samples

                                     Fig. 1
                                     ____ _


On every pass the  number in FREQUENCY is  sent to the GENERATOR  which produces
the sample passed as the output.  But the FREQUENCY number is also  piped around
and added into the number coming down from SLEW.  The sum of FREQUENCY  and SLEW
replaces the old value in FREQUENCY.  But SLEW doesn't change, it's  a constant.
You can  see that  this thing can  run forever  by itself.  On  each pass  a new
frequency is formed and a new sample is shipped out.

Since we'd like to be able to change its values from time to time,  lets declare
that there will be two ticks per pass, one processing tick, and one update tick.
As  we begin  looking at  the  processing stream,  we notice  it's  already been
running and that the last output was 1000 and SLEW had 1 in it.  If this went on
as  is,  we  would  send succeeding  values  of  1001,1002,1003...  etc.  to the
GENERATOR.  But let's  say that we're  tired of this and  want to change  it, so
we'll put 3 in SLEW at our first chance (where the first vertical arrow is), and
we'll change FREQUENCY to 500 at our next chance (at the second arrow).  The two
rows SLEW and FREQ. in the next figure show the progression of values of the two
settable locations in our generator.   The bottom line shows what  frequency the
generator is actually producing on that pass.

        |<--- pass -->|<-- pass --->|<--- pass -->|<--- pass -->|
        |             |         ↓   |        ↓    |             |
        |proc| |upd.| |proc| |UPD.| |proc| |UPD.| |proc| |upd.| |
        |____|_|____|_|____|_|____|_|____|_|____|_|____|_|____| |
Slew    |   1| |   1| |   1| |   3| |   3| |   3| |   3| |   3| |
        |____|_|____|_|____|_|____|_|____|_|____|_|____|_|____| |
Freq.   |1001| |1001| |1002| |1002| |1005| | 500| | 503| | 503| |
        |____|_|____|_|____|_|____|_|____|_|____|_|____|_|____| |
Out     |1000|        |1001|        |1002|        |1005|        |503|...

                                     Fig. 2
                                     ____ _




                                     Page 4
LRNSAM                                                                     DRAFT


                                   Section 4
                                   _______ _

                             Tick time requirements
                             ____ ____ ____________





4-1. Generator tick time

Each running generator requires one processing tick; the length of the generator
pipeline is 9 ticks.

So if we had, say, 11 generators running, and assumed for the moment  that there
were no update ticks, how many ticks would it take before we got a  complete set
of valid results for all the  generators?  That is to say, when would  the first
pass end?  It would end after 20 ticks, right?  If not right, go back and reread
the part about  pipelining again in  appendix 1.  When  would the next  pass and
all subsequent passes end?  Right,...  after 11 more ticks.  So the  formula for
processing ticks required by each running generator is:

   <# of running generators> + 9.                                         Equ. 1



4-2. Modifier tick time

Modifiers do general numerical and logic processing.  As the name  implies, they
modify existing  numbers.  In fact,  they are supplied  with two inputs  and one
output (See section  17).  Since modifiers have  to do two memory  references to
get input data from sum memory where generators do but one (see section  9) they
require two processing ticks.  However,  the length of the modifier  pipeline is
also nine ticks.  So  say we had 7 running  modifiers with no update  ticks, how
long until the first complete set of valid numbers comes out?  The answer is 23.
So, the tick requirement for modifiers is:

   2*<# of running modifiers> + 9.                                        Equ. 2



4-3. Combined tick times

Now let's take  an example where we  have both modifiers and  generators running
simultaneously (still without update ticks).  We must first know that  there are
really two processors,one  of which represents the  modifiers and the  other the
generators.   These  are  separate nests  of  electronics  that  run essentially
independently of each other (although they are actually in lock step).


                                     Page 5
LRNSAM                       Tick time requirements                        DRAFT


How many processing ticks for the first pass would be required if we  have, say,
11 running generators and 5 running modifiers?  The correct answer is  20 ticks.
The tick requirement for  the first complete set  of modifier output is  19, but
that for generators is 20.  Since they are parallel processes, the modifiers can
do their whole thing in 19 ticks while the generators are also running.



4-4. General formula

So the official formula reads as follows, and I quote: "The number of processing
ticks is nine more than the maximum of: the number of generators used; twice the
number of modifiers used."(1) Or,

total proceessing ticks =
        2*<# modifiers used> MAX <# generators used> + 9.                 Equ. 3

The Delay units have their own formula for tick requirements which is  not shown
here.  See Appendix 7.

One last word about processing  and update ticks.  References to sum  memory and
delay memory are done on processing ticks, not update ticks.  Update  ticks only
refer to data coming in from outside the Samson box.























_______________________
(1) Systems Concepts Digital Synthesizer Programming Specification .
    _______ ________ _______ ___________ ___________ _____________ _

                                     Page 6
LRNSAM                                                                     DRAFT


                                   Section 5
                                   _______ _

                         Processing speed vs. capacity
                         __________ _____ ___ ________





5-1. Speed of processing

The number of ticks per pass is set by the programmer.  This is so because there
is a playoff between passes and the two kinds of ticks.  The nut of  the problem
is that, when all  is said and done, the  basic speed of processing one  tick is
                                                    -7
195 nsec.   (That's .0000000195 sec.,  or 1.95  x 10  , or  195 billionths  of a
second.) That's the time  that it takes a  processing element to do  the slowest
thing it  has to  do.  So  the whole  box is  geared to  go at  this rate  by an
internal clock.  This means that the maximum number of ticks we can have  in one
second is 5,128,205.128.

Now,  as you  recall, the  whole purpose  of the  box is  to produce  bunches of
numbers which are passed at blinding speeds to the DAC.  Each number sent to the
DAC represents a complex calculation  of the value of whatever waveform  the box
is  producing.  Taken  through  time, the  individual  samples sent  to  the DAC
produce  the desired  waveform.  The  samples must  be passed  to the  DAC  at a
uniform  rate, and  at  a sufficiently  high  speed so  as  to get  a  good high
frequency response.  For instance, a typical sampling rate of 12800  samples per
second produces  a usable frequency  response from 0  to 6400 Hz.   Faster rates
produce higher frequency  responses.  So naturally we  want a good  fat sampling
rate.  But we've got to watch out for a catch, namely, that the box can  only do
so much so fast.  The barrier is the 195 nsec. tick turn around time.



5-2. Ticks per pass

The problem should become clear when we  try to feed samples to the DAC  in real
time.  Real time, in this example  means that we will attempt to  guarantee that
the box  will send 12800  samples to the  DAC per second.   That means  one pass
                 -5
takes 7.8125 x 10    seconds.  Furthermore, it must  send them at an  even rate,
such that each pass gets computed and each sample shipped out in 1/12800'th of a
second.  How many  ticks can we  squeeze out of this  much time?  That  comes to
           -5              -7
7.8125 x 10    / 1.95 x  10   = 400  ticks per pass.   This then is  the maximum


                                     Page 7
LRNSAM                   Processing speed vs. capacity                     DRAFT


number of  ticks of  either kind  that can be  squeezed into  a pass.   That's a
pretty  good number,  actually.  That  means we  could have  all  the generators
running using up 256 ticks and still  have 144 left as update ticks.  But  if we
were really greedy, and wanted even more ticks than this at this  sampling rate,
we would  no longer  be able to  guarantee a  sample on each  pass and  we would
sooner or later  (probabily sooner) fall behind.   This is called  data underrun
and when this happens, we drop out of real time.  Another way to say it  is: you
drop out  of real time  if the duration  of the pass  period is larger  than the
sample period.

This is called processing degradation.  It's not altogether catastrophic in that
there are several ways to  suffer this gracefully if it becomes  necessary, such
as to  1) lower  the sampling rate  thereby increasing  the sampling  period and
hence the pass period, 2)  lower the complexity of the process  thereby reducing
the number of ticks required and shrinking the pass period, or 3) to accept less
than real time processing and pass the samples to some form of memory (probabily
the disk) from whence they can be played as a whole when the processing is done.
(Even here there are limitations in  that the disk bandwidth, i.e.  the  rate of
data transfer, becomes  your limitation.  Disk I/O  is currently the  number one
bottleneck in the A.I. system.)



5-3. Processing vs. update ticks

Not only is there a playoff between the desire to have the sampling rate as fast
as possible and the desire to have moby amounts of ticks per pass, but  there is
a playoff between the  two kinds of ticks,  to wit: Processing ticks  and update
ticks occupy mutually  conflicting time domains.   All the processing  ticks are
performed first,  then what's left  is given  to the update  ticks.  So  if your
piece requires lots  of real time user  interaction with keyboards or  what have
you,  or if  you are  repatching  the connections  and modes  of  the processing
elements all the time, then you will be forced to allocate a larger part of each
pass to sending  update information to  the box, and  that means lots  of update
ticks per pass.   And since no  processing can be  done in a  processing element
while it is having its parameters updated, this reduces the number of processing
ticks  available on  any given  sample, and  means that  the instrument  must be
simpler than one which had fewer  update ticks.  The obverse works as  well: too
complex an instrument means you have fewer update ticks per sample.  So,  as old
Richard Almaniac used to say, "budget your ticks wisely!"



5-4. Budgeting ticks

Now when I said above that the  user sets the number of ticks, what  that really
comes down to is that the user  sets two numbers, the total number of  ticks per


                                     Page 8
LRNSAM                   Processing speed vs. capacity                     DRAFT


pass,  and the  total number  of those  ticks which  are processing  ticks.  The
remainder are, of  course, update ticks.  Now  in figuring this number,  we will
ignore  the nine  ticks tacked  on  to represent  the length  of  the processing
element pipeline.  So  we really only  need to add  up the number  of generators
used, take the maximum of this number or twice the number of modifiers, plus the
number of update ticks we expect to need.  This will get the number that we will
actually send off to the box to be the total number of ticks per pass.   It will
take  this number  and compute  the sampling  rate that  it equals,  and  set up
everything accordingly.  Actually, the number arrived at in this way may  or may
not correspond to an existing low  pass filter implemented in the box.   In that
case, you get the filter whose cutoff frequency is less than or equal to 1/2 the
sampling rate calculated as above.



5-5. Numbering Processing elements

One final  word of  note: the  processing elements  are all  numbered ordinally.
Generators go from 0 to 255 modifiers go from 0 to 127. Processing always begins
with the lowest numbered running processing element and goes  sequentially until
the end of the pass.  Since speed is typically such an important commodity here,
it  is  important  to  always  use  the  lowest  numbered  consecutive  block of
processing elements as possible.  If you have a processing element in there that
is really not being used but its number is below the highest one in use, you are
wasting compute time (sinful!).
























                                     Page 9
LRNSAM                                                                     DRAFT


                                   Section 6
                                   _______ _

                            The processing elements
                            ___ __________ ________



As you have  seen, the Samson  box has two  basic types of  processing elements:
generators and  modifiers.  There  is an  array of  memory locations  called sum
memory in  the box  which these processing  elements use  to pass  numbers among
themselves.  Also  contained within it  is a large  memory called  delay memory.
This memory is accessed by the  modifiers and can be used as delay  lines (hence
the name) for  realizing filters and reverberators.   The delay memory  can also
function as table lookup  memory allowing various computer-loaded  functions and
tables to be easily accommodated.



6-1. Generators

The generators perform two functions: waveform generation and  dynamic amplitude
control  of the  generated  waveform.  The  portion that  performs  the waveform
generation is called the oscillator, and the portion that handles  the amplitude
control we'll call the envelope controller.  Generators can produce sine, square
and saw-tooth waveforms,  pulse train, and equal-amplitude  sum-of-cosines (band
limited pulse  trains).  They can  be frequency modulated  by the output  of any
other processing  element, and the  frequency can also  be swept  linearly.  The
envelope side has facility to shape amplitude using either linear or exponential
curves.  There are 256 generators,  and they can all be  running simultaneously.
Which  of  these various  operations  the  generator does  is  dependent  on the
generator's mode, which is set during update tick time.



6-2. Modifiers

Modifiers can  act as filters,  amplitude modulators,  four-quadrant multipliers
(signed multiply), sample mixers  and uniform noise generators (white  noise and
random number generation). They can  also do signal latching (sample  and hold),
sign testing, signal zero-crossing detection, and minimum or maximum testing.

Modifiers  are  also  used  to  pass data  to  and  from  the  delay  memory for
reverberation or table lookup.  The box contains 128 modifiers all of  which can
be running at once.  The mode is likewise (and for all processing  elements) set
on update tick time.





                                    Page 10
LRNSAM                      The processing elements                        DRAFT


6-3. Sum memory

Information is passed among generators and modifiers through what is  called sum
memory.  This can be thought of  as a kind of telephone switch-board  because it
is through the  sum memory that  the data put out  by one processing  element is
accessed by other processing elements.  Sum memory is accessed during processing
tick time.



6-4. Delay memory

Delay memory  has two uses:  It can simulate  delay lines for  reverberation and
filtering, and it can store precomputed tables, such as time-domain waveforms or
mathematical functions that would not be possible (on our budget) to  have built
in.  A port  into delay memory  consists of a  modifier in DELAY_LINE  mode (see
section 17 for modifier modes) and a delay unit.  There are 32 delay  units, and
they can all be running at once.































                                    Page 11
LRNSAM                                                                     DRAFT


BLKDIA.XIP[DOC,MUS] goes here.
















































                                    Page 12
LRNSAM                                                                     DRAFT


                                   Section 7
                                   _______ _

                                The flow of data
                                ___ ____ __ ____





7-1. The Connection...

The interface of the Samson box to  the world at large is roughly this:  we, the
users, speak to the PDP-10 which  runs the AI timesharing system and  the PDP-10
speaks  to the  Samson  box.  It's  not  that simple,  there's  another computer
between the 10 and the box, but since it is transparent to the  overall process,
we'll leave it out here. Also connected to the Samson box are the DACs  that get
the samples the box produces.

The following is  a list of the  different kinds of things  we would like  to be
able to pass between these devices: we will need to be able to 1)  send commands
from the  general timesharing  system, 2) read  back diagnostic  information, 3)
read and write sound  samples from and to the  system, and finally, 4)  to write
samples to DACs.  But  before we go into greater  detail, a caveat must  be made
about  the  discussion  of  the  first three  items.   What  is  presented  is a
description of a "typical" usage of the Samson box rather than a  description of
what actually is being done at SAIL because the actual hardware  connection here
is likely  to go  through many transformations  in the  course of  time, besides
which a detailed review of the subject is beyond the scope of this manual.  (For
those interested, or if your work requires it, see a wizard.)



7-2. Command path

We  will need  a command  path from  the PDP-10  to the  box to  pass processing
element instructions, like start  and stop instructions, connection  plans, mode
changes, and the  actual data to  be put in the  processing elements to  set the
frequency of generators, etc.  All  of this information is passed  during update
tick time.   To do this  there is a  direct memory access  channel (DMA)  set up
between the box and the main core memory in the PDP-10.  The  central processing
unit (CPU) in the PDP-10 first finds a free block of core memory, then  fills it
with instructions and update  data for the box,  then passes the address  to the
box.  Then the box, during the  processing of update ticks, reads it  in through
the DMA channel.






                                    Page 13
LRNSAM                          The flow of data                           DRAFT


7-3. I/O path

Next, we will need a way to set the general status of the box, to turn it on and
off, and to  pass it the  address of the core  memory discussed above.   This is
called the I/O (for in/out) path.  It is a relatively slower path than  the DMA.
It is also used for diagnostic readback.



7-4. Read path and Write path

Here we want to take  a sample of sound from  the main computer and put  it into
sum memory so it  can be acted on by  the processing elements.  We can  think of
the sample as just more update information.  We have a problem,  however: notice
that sum memory can only  be addressed by processing elements.  The  solution is
to put some processing element into  a special state whereby it just  takes it's
update information and passes it directly to sum memory.  Then and only then can
other processing elements reference that  sample to modify it or  whatever.  The
generators were chosen for this task and the implementation is discussed later.

To return sound  samples back to  the computer we  have the same  problem.  Only
processing elements can read sum memory.  So again the generators have a special
mode  that reads  sum memory  and  passes the  data to  the DMA  channel  to the
computer.



7-5. DAC path

Lastly, how do  we get samples from  out of the box  to the DACs?  Again  we use
generators to read sum memory and pass the data to a bus to the DACs.



7-6. It's a Stream machine...

This is an appropriate time to  mention that the box is a "stream"  type device,
meaning that it  can be running whether  you are telling it  what to do  or not.
You don't have to fill up all  the update ticks that you've asked for.   You can
send  less  PROVIDED  that you  tell  the  box not  to  expect  any  more update
information until  some time which  you specify  as being so  many ticks  in the
future.  This  number can either  be relative  from the first  tick or  from the
current tick.  Thus you can set up some state in the box and tell it to continue
doing what it is set up to do.  The way to do this is to give it a DWELL command
(See page  69).  This  command is passed  to the  box on an  update tick  and it
instructs the box not to process  any more update commands until it has  done so
many ticks.  If you don't give the box an instruction when it expects  one, it's


                                    Page 14
LRNSAM                          The flow of data                           DRAFT


called "command underrun", and it can stop the box.  It then sends  an interrupt
to the system which halts your program.  Actually, any process that gets  out of
sync with  its environment will  cause or suffer  underrun or  overrun.  There's
less likelihood that anything inside the box would get out of order (heaven help
us!), than between  the system and  the Samson box where  things can get  out of
hand very easily.  If it is  trying to get commands while the system  is looking
elsewhere, or if there is a disk bottleneck (not unlikely) then you  get command
underrun. There  are other  data errors that  can also  send interrupts  such as
arithmetic  overflow  and  problems  relating  to  direct  memory  access.   All
interrupts are handled through the I/O path.







































                                    Page 15
LRNSAM                                                                     DRAFT


                                   Section 8
                                   _______ _

                                   Sum memory
                                   ___ ______



Sum  memory  is  the place  that  numbers  are stored  that  are  the  output of
processing elements and are to be the input to other processing elements.  It is
necessary that a value  be read into a location  in sum memory before it  can be
read by any other processing element.   Sum memory is made up of  256 locations,
numbered from 0 to 255.  Addresses are passed to those processing  elements that
are to read or write a number into that sum memory location.

It's called sum memory because any value added in during a pass is added  to the
contents already there from the beginning of that pass.



8-1. This pass - last pass division

Sum memory is divided in two  basic ways, producing four quadrants.  One  of the
divisions is  between "this pass"  sum memory and  "last pass" sum  memory.  The
active processing  elements on  the current pass  all write  into this  pass sum
memory and read from  last pass.  This means that  all the outputs of  any given
pass  are  in  place before  any  reading  of sum  memory  is  done.   After the
processing of all  the outputs has taken  place (which, you should  remember, is
the definition of a pass), this pass sum memory is frozen and becomes  last pass
sum memory.  This is  nice in that it means  that you don't have to  worry about
the order of computation  of the processing elements  inside of a pass.   By the
time you  get around to  reading the  output of any  processing element  all the
outputs of all the processing elements are available, ensconced in last pass sum
memory.

However, there is a special feature that modifiers can also read from  this pass
sum memory (not  to make things  too confusing or  anything), in which  case the
ordering of the modifiers inside a pass does become critical.  If, for instance,
                                        ____
the modifier that is trying to read a this pass sum memory location  is executed
before the one that is supposed to be supplying the number, you get zeros.

Please notice an implication  of the separation in  time by one pass  of reading
and writing into sum memory.  Whereas the processing order within a pass  is not
critical  (with the  exception above  noted), the  processing between  passes is
                                                                              __
critical if you are chaining a bunch of processing elements together.
Say maybe you want  to FM modulate an  FM modulated generator, meaning  that you
have the output  of one generator affecting  the frequency of  another generator
affecting the  frequency of yet  another generator.  This  setup is  yet another
pipeline!  So you must be sure to start the processing elements in  order.  Pass


                                    Page 16
LRNSAM                             Sum memory                              DRAFT


1: the three generators crank and  from generator 1 comes a valid number,  but 2
and 3 produce nonsense; pass 2: generator 2 reads generator 1's output and gives
a valid  result; pass 3:  finally the valid  results have trickeled  through the
system.  Had we  not started up  the generators in order,  we would have  lost a
sample and  promulgated the  delay down  through the  line.  It  may or  may not
produce a fatal error, but it is surely a potential bug.

The last pass and  this pass halves of sum  memory alternate, one set  being the
current this pass, then becoming  last pass.  Before it again becomes  this pass
it is  erased so  that previous values  don't linger.   Therefore reading  a sum
memory location without writing in it first will always return zero.



8-2. Generator - modifier division

The other basic division of sum  memory has to do with which kind  of processing
element writes where.

One half of "this pass" sum memory is dedicated to output from generators.  That
means the generators  have 64 locations  in this pass  sum memory that  they can
write into.  The other  half of  this pass  sum memory  (64 locations  again) is
dedicated to the output of modifiers.  For this pass sum memory this distinction
remains firm.  However, either type of processing element can read  any location
                                                              ____
(128 of them) in last pass sum memory.

The modifiers have the ability, you  recall, to read from this pass  sum memory,
however, they are restricted to reading from the modifier quadrant.   That makes
a total of  192 locations that are  available for them to  read from on  a pass.
This can be so  since a modifier only does  one write into sum memory  every two
ticks. So it has some extra time  to do sum memory reads.  Again, if  a modifier
is  reading from  this  pass memory,  the ordering  of  the units  must  be done
carefully so that  one is assured  that the data is  present in memory  when you
want to read it. This requires keeping track of the extensive pipelining  in the
machine, which we will not go into here.



8-3. Addressing sum memory

Here's what the  processing elements have to  communicate with sum  memory: Each
generator  that is  running is  given a  location in  sum memory  into  which it
deposits its  generated sample.  More  precisely, there is  a location  in every
running generator which contains a word that is the address of a location in sum
memory into  which that generator  is to  put its output.   The location  in the
generator that stores  the sum memory  address is called  (appropriately enough)
SUM_MEMORY.  Generator location SUM_MEMORY  is therefore called a "port"  to sum


                                    Page 17
LRNSAM                             Sum memory                              DRAFT


memory.  Generators likewise  have one port to  read from sum memory  called FM.
This means that FM is a location in the generator which contains a word which is
an address of a location in sum memory from which the generator is to read data.

The modifiers have one port to write, also also called SUM_MEMORY (the assembler
can make the distinction of whether we are referring to the  SUM_MEMORY location
in a modifier or generator as  will be explained later), and two ports  to read,
A_IN and B_IN.

The delay units have no direct port  to the sum memory, but must be  accessed by
the modifiers, which pass data to  the delay units and read data from  them. The
modifiers then transfer data to sum memory.





































                                    Page 18
LRNSAM                                                                     DRAFT


                                   Section 9
                                   _______ _

                                   Generators
                                   __________



Generators are one of two main processing elements in the box.  They  do various
things, most important  of which, they  produce oscillations of  various flavors
and frequencies and they control the amplitude of the oscillations through time.
The generators are divided into two parts: the oscillator is the side that makes
the waveform, and the side that controls the amplitude of the waveform is called
the envelope controller, or just envelope.

The generators generate waveforms and envelopes by two basic methods  1) logical
testing of a changing function (as  in the case of pulse train and  square wave)
or 2) table  lookup (as in  the case of  sine waves and  exponential envelopes).
The oscillator  looks up  values from a  sine table  to produce  sinusoidal (and
cosinusoidal) waveforms.  There are  several things it must determine  before it
knows where  to look in  the table,  most obviously what  the frequency  is, the
phase, etc., etc.

The information the generator needs to  produce its waveform is passed to  it on
update ticks.  The values sent  are stored in locations in the  generators, from
whence  they  are  combined  by  processing  ticks  to  produce   the  waveform.
Generators can  receive new parameters  for all of  the locations listed  in the
table below on the  processing of one update  tick.  Depending on what  mode the
generator is  in, not all  of this information  may be needed  or useful  at any
given time.  In  addition, some locations have  multiple functions and  are used
for different things  in different modes.  (For  instance, if you just  want the
generator to produce  sine waves, you need  not specify NCOSINES.  If  you don't
care about the initial phase of the wave, you need not specify ANGLE, etc.  More
on this later.)

Although the oscillator part of the generator will be covered more  fully later,
a little of its operating  theory is necessary before discussing  its variables.
The heart of the  oscillator side of the generator  is the ANGLE term.   This is
the index which keeps track of where we are in the cycle of whatever waveform we
are producing.  Angle is what is tested or used for table lookups that determine
the value of the  sample.  ANGLE should be  thought of as an  unsigned binary(1)
counting register that, as it is incremented from its lowest number  (all zeros)
to  its highest  (all ones),  represents the  phase angle  of one  cycle  of the
waveform being generated.  The phase  angle of zero is represented by  all zeros
in the register, pi/2 is represented by 010000... (decimal.25, i.e.  one quarter
of a cycle), pi by 10000...(decimal .5, the half-wave point), and when  ANGLE is

_______________________
(1) for a discussion of number representations used in this manual  see appendix
3.

                                    Page 19
LRNSAM                             Generators                              DRAFT


all ones,  the phase  angle is  about ready  to start  another cycle.   ANGLE is
mostly formed of the  number in FREQUENCY plus  whatever was in ANGLE  from last
pass.  As  FREQUENCY increments ANGLE,  ANGLE rises until  it reaches  the point
where the  next value from  FREQUENCY would overflow  the register,  then modulo
arithmetic sets in as follows:  the ANGLE register is cleared and  the remainder
magicly appears as its new value, and  we have started a new cycle of  our wave.
Thus the size  of the number in  FREQUENCY determines the  frequency (oftenness)
that ANGLE turns over.  The EXPONENT  term in the oscillator side does  much the
same thing as ANGLE.  All this is explained in greater detail later.



9-1. Generator parameters

In the tables below, there are  generally two names for each location.  There is
the internal name (denoted  by the column marked Id)  which is the name  used in
the  hardware description  of the  device and  on the  circuit diagrams  for the
device, then there  is the SAIL name  (denoted by the column  marked Definition)
which is how we refer to these things in SAIL.  I will refer alternately  to the
Id name  and the  Definition in the  discussion of  these things  later, usually
puting the Id  in parenthesis after  the SAIL name,  to acquaint you  with both.
But  you should  understand that  the Definition  is what  you will  use  in any
                                      __________
program to refer to the particular  location in the box, and the  assembler that
actually runs the  box will convert  it into the  equivalent Id code.   The Size
column tells  how many binary  bits the location  consists of.  The  Type column
says whether the  number is: Signed, Unsigned,  Different in different  modes or
                             _       _          _
Inconsequential  (can  be considered  either).   The notations  in  the Function
_
column are elaborated more thoroughly later.





















                                    Page 20
LRNSAM                             Generators                              DRAFT


Id    Size Type Definition Function
--------------------------------------------------------------------------------
GO    20   S    SWEEP      oscillator frequency sweep rate
GJ    28   I    FREQUENCY  oscillator frequency
GK    20   D    ANGLE      oscillator angle
GN    11   U    NCOSINES   number of cosines to be summed
GM    4    U    SCALE      binary scale of sine or sum of cosines
GP    20   S    RATE       envelope rate of change
GQ    24   U    EXPONENT   envelope current value
GL    12   U    ASYMPTOTE  asymptote (DC offset of the envelope)
GSUM  6    U    SUM_MEMORY sum memory location into which output is added
GFM   7    U    FM         sum memory address from which frequency
                           modulation data is taken
GMODE 10        MODE       generator mode, including run mode and
                           type of oscillator and envelope processing
                           (see list of modes on page 22).
                OSC_MODE   sets oscillator field of SUM_MEMORY without
                           altering run mode or envelope mode.
                ENVELOPE   sets envelope field of SUM_MEMORY without
                           altering run mode or oscillator mode.

                                     Fig. 3
                                     ____ _



























                                    Page 21
LRNSAM                                                                     DRAFT


                                   Section 10
                                   _______ __

                              Generator run modes
                              _________ ___ _____



This is  a list  of general modes,  affecting both  the envelope  and oscillator
halves of the generator.  There  are some additional modes that are  specific to
the two halves which we'll get to  in good time.  The run modes are set  up with
update ticks.  Since the hardware names for the run modes are binary words, only
the SAIL definitions are given.

Definition         Osc. run? Envelope run? Add to sum?
--------------------------------------------------------------------------------
G_INACTIVE         no        no            no
G_PAUSE            no        no            no
G_WAIT             yes       no            no
x_RUNNING:
   A_RUNNING       yes       yes           yes
   B_RUNNING       yes       yes           yes
   C_RUNNING       yes       yes           yes
DATA_READ          no        yes           yes
DATA_WRITE         no        no            no
DAC_WRITE          no        no            no

                                     Fig. 4
                                     ____ _




10-1. INACTIVE

freezes  both  the oscillator  part  of  the generator  and  the  envelope side.
Actually,  the  generator  continues  to  be  processed,  but  the  changing  of
parameters is inhibited and it produces no output.  Please note that  this means
making  a generator  INACTIVE does  not clear  its memory.  It retains  the last
frequency and envelope it had  before going inactive. In fact,  generator memory
is  never cleared  except by  explicit  command to  load new  values.   For this
reason, you have to  set all the relevant  parameters of a generator  before you
turn it on, else you might be subject to a certain form of astonishment.



10-2. G_PAUSE

freezes both  the oscillator  and the envelope  as does  INACTIVE, but  makes it
easier to restart it.  There is a special command called "clear all  pause bits"


                                    Page 22
LRNSAM                        Generator run modes                          DRAFT


(see page  68) that  will reinstate  all PAUSing  generators, whereas  you can't
reinstate an INACTIVE generator except by an explicit change-mode command.  This
is useful if, for instance, a bunch of generators were running, you want them to
stop,  but you  don't  want to  have to  send  them all  individual  mode change
commands to crank them up one  by one which would waste update ticks.   This way
also you are guaranteed that they will all restart later with the same phase and
envelope positions  as where they  left off.  (But you must  set up  the pausing
generators one per update tick.)



10-3. G_WAIT

continues running the oscillator side but turns off the envelope side  and stops
output. This is to  let the oscillator keep  in phase with any  other generators
that may be running concurrently.   It can therefore reenter later and  still be
in sync.  with the rest  of the world.   There is also  a "clear all  wait bits"
command (see page 68) later that encourages the use of G_WAIT mode.



10-4. x_RUNNING
This is  the principal running  mode.  It has  three slightly different  ways of
doing its thing.  Depending on which one you want, you substitute A, B or  C for
the x.  Basically, the  only difference is in how  the envelope side is  set up,
the oscillator in all three modes just runs as usual.
But first  a word  about stickiness.   Imagine the  following horror  story: the
envelope part of the generator is happily running along cranking out ever louder
samples.  Comes a  time when this  is supposed to  stop, but where's  the update
command?: stuck somewhere in  the operating system which  is too hung up  to get
the  command out.   The amplitude  keeps growing  until finally,  we  exceed the
largest amplitude the  generator can produce  and overflow...  What  a bummer...
(1)
But, the envelope side of the  generator can be made "sticky," which  means that
rather than overflow for lack of a command to tell it to stop, the envelope side
of the generator will "stick" at the last value it attained before it would have
overflowed. From that point on it will just continue to return that number until
the update arrives  to tell it to  do something else.  Alternately  the envelope
can be set to "free" mode, in which case the envelope wraps around and starts in
at the beginning again, and all ones become all zeros and we start  rising again
(or all zeros become ones and we continue falling).  A_RUNNING sets the envelope
in sticky mode.  B_RUNNING sets it in free mode.

_______________________
(1) Arithmetic overflow occurs when  the maximum positive number is  exceeded or
the maximum negative  number is exceeded. For  instance, if you add  anything to
the  binary number  11111111111  . .  . you  get  an overflow.  Likewise  if you
subtract anything from the binary number 000000000 . . . you get an overflow.

                                    Page 23
LRNSAM                        Generator run modes                          DRAFT


There's another mode whereby arithmetic overflow in an envelope can  be detected
and used as a "trigger" to cause the next numbered (the subsequent) generator to
go from G_WAIT  to running.  Meanwhile the  triggering generator goes  to G_WAIT
mode.   This  is what  C_RUNNING  does.  The  purpose  of C_RUNNING  is  that by
dedicating,  for  instance,  three  sequentially  numbered  generators   to  one
"instrument", such that one generator  computes the attack, the next  the steady
state, and the  last the decay,  the "instrument" can,  once it is  started, run
without  any  further  update  commands.  This  ties  up  three  generators, but
lightens  the  command  stream  by  two  update  ticks  per  invocation  of that
instrument.  It may also be useful  in cases like the one described  above where
the general system load is such that  the box couldn't get a command in  time to
save a generator from command  underrun.  The user would still have  a guarentee
that at least  the note once started  would continue along a  prescribed course.
Thus we achieve graceful degradation.



10-5. DAC_WRITE

mode makes this oscillator be  a port between the sum memory  location addressed
by the word in FM  and a DAC addressed by the  word in SWEEP (GO).  This  is how
you get sounds  out of the  box.  In this capacity  the generator is  similar to
OUTn locations in MUSCMP.  (MUSCMP  is the generic name for your  favorite music
compiler.) What happens to SWEEP  (GO)?  This is an instance where  the function
of the  generator modifies what  the variables mean.   When the generator  is in
DAC_WRITE mode, it is no longer  a generator in the sense of  producing samples.
It goes into the special mode of taking information from the sum memory location
whose address  is in FM  and passing it  to the DAC  whose location is  in SWEEP
(GO).  All other variables in this generator have no effect in this mode.



10-6. READ_DATA

mode makes the  generator read data from  main computer core memory  through the
DMA channel, and deposit it in the sum memory addressed by SUM_MEMORY.   This is
how we get  sound samples (for  instance) into the  box from some  other device.
The DMA channel is set up with a SET_OUTPUT command (discussed later).   The CPU
figures out how to get the data to the generator.  It is important to  note that
the write data will be interleaved in generator number order.
                                      _________



10-7. WRITE_DATA

makes the generator take samples from a sum memory location and write  them into
main computer core memory through the DMA channel.  This is how sound  files are


                                    Page 24
LRNSAM                        Generator run modes                          DRAFT


written onto the disk.  You set up one generator in WRITE_DATA mode  per channel
of output.  Again, the write data is interleaved in generator number order.
                                                    _________















































                                    Page 25
LRNSAM                                                                     DRAFT


DSK:GENDIA.XIP[DOC,MUS] GOES HERE
















































                                    Page 26
LRNSAM                                                                     DRAFT


                                   Section 11
                                   _______ __

                             Oscillator processing
                             __________ __________



In the diagram on page  26, the left side represents the  oscillator processing,
the right  half the envelope  processing.  SWEEP (GO),  RATE (GP)  and ASYMPTOTE
(GL) are constants  which remain until altered  by command, all  other locations
are  "refreshing",  meaning  that  their  value  is  recomputed  every  time the
generator goes through a processing tick.  The old value is read by  the process
just below  it, and the  old value is  added to the  new value coming  down from
above.  This is diagrammed as an  arrow returning from the bottom to the  top of
such locations such as ANGLE (GK).

If an  oscillator is in  a wave  producing mode, the  following is  a simplified
description of what happens:(1) On the oscillator side, FREQUENCY (GJ) will have
the current frequency of the oscillator. SWEEP (GO) is a constant that  is added
into FREQUENCY (GJ) every pass  to augment or diminish the frequency  to produce
sweeping or glissando.  (If you do not  want any glissando, you  must explicitly
zero SWEEP.)

FM is the return port into the oscillator from the "last pass" sum  memory. When
the  oscillator is  in any  oscillatory  mode (as  opposed to  the  data channel
modes), FM reads  the location in  sum memory and  the returned value  is summed
with FREQUENCY thereby adding a modulating frequency (assuming that  a frequency
was put, on  the last pass,  into the sum memory  location that FM  reads).  The
value in  FREQUENCY is  not changed  by the  addition with  FM.  Note  that each
generator, when in an oscillatory mode, always takes in a number through FM!  If
                                        ______
you don't want anything here, then you  have to give FM the address of  some sum
memory location that is unused, so it will always return zero.

ANGLE (GK) stores the current angle.  It is updated by adding FREQUENCY  (GJ) as
computed above to the number in ANGLE (GK) left from last pass.  ANGLE (GK) then
contains the current oscillator angle.  It is used, depending on the mode of the
oscillator to determine the value of the output sample.  It can be considered to
be the momentary  phase angle of the  waveform the generator is  producing.  But
the angle is not in terms of radians or degrees. It's simpler, actually:  as the
number in ANGLE goes from all 0000...'s to all 11111...'s we complete  one cycle
of our waveform. That  is we go in radians  from 0 to 2π.  After  11111... ANGLE
wraps around to 00000... again.  Thus  when the number in angle is  01000..., we
are  at  the half-wave  point.   Likewise, RATE  increments  EXPONENT  such that
EXPONENT goes from zero to full amplitude as its bits go from 0's to 1's.

When we finally have the value  in ANGLE, what happens next depends on  the mode

_______________________
(1) For the real version, see the Specification.

                                    Page 27
LRNSAM                       Oscillator processing                         DRAFT


of the oscillator.  If  it is in a sine  wave producing mode, the ANGLE  (GK) is
used to lookup  a value on  a sine table.   This is then  the end result  of the
oscillator side.  This result  is not complete,  however, as  the output  of the
oscillator  must still  be scaled  by the  envelope side  of the  generator. The
envelope side is discussed  in the next section, and  the rest of the  modes are
covered in more detail on page 36.











































                                    Page 28
LRNSAM                                                                     DRAFT


                                   Section 12
                                   _______ __

                                Oscillator modes
                                __________ _____



These modes set the type of  waveform the oscillator side of the  generator will
produce.

   Definition      Function
--------------------------------------------------------------------------------
   SAWTOOTH        sawtooth wave
   SQUARE          square wave
   SUM_OF_COSINES  sum of cosines
   PULSE_TRAIN     pulse train
   SINE            sine(ANGLE),normal sine wave
                   mode (with fm).
   SIN_FM          cosine(FREQUENCY + FM)
                   special sine table lookup mode.

                                     Fig. 6
                                     ____ _




12-1. SINE

This mode is the standard sine wave oscillator with an fm input.  It can be made
into a cosine oscillator by setting the ANGLE initially to π/2, which  in binary
would be 010000... (unsigned), the quarter-wave point.



12-2. SAWTOOTH

For this,  the oscillator merely  has to pass  on the oscillator  angle directly
since  the  angle consists  (for  positive frequencies)  of  an  increasing ramp
function.  The wave starts at whatever  you set ANGLE to be (maybe  00000...) it
goes  from  there to  11111...,  then  wraps around  to  00000...  and continues
increasing. (It would be decreasing if the FREQUENCY were negative.) Since ANGLE
here is considered to be the actual waveform passed by the oscillator, we should
think of the ANGLE term as being a two's complement signed number since  we want
to represent the waveform as  having a positive and negative domain  as acoustic
waves do.  Remember, the number  from the oscillator side, regardless of  how it
is  arrived at,  is  multiplied by  the output  of  the envelope  side  in two's
complement  form.   This  illustrates  how ANGLE  can  be  considered  signed or
unsigned depending on how it is used.  When ANGLE is used to look up values on a


                                    Page 29
LRNSAM                          Oscillator modes                           DRAFT


sine table the sine of ANGLE =  0000... will be zero no matter how  we interpret
ANGLE.  But it must be interpreted as a two's complement number if we  intend it
to be the actual result of the oscillator.



12-3. SQUARE

The  oscillator produces  square waves  by simply  testing the  value  of ANGLE.
while  ANGLE  is  ≥  10000...(unsigned .5)  the  oscillator  produces  the value
01000...  (signed).   When ANGLE ≤  01111...(unsigned), the  oscillator produces
11000...(signed -.5).   Again, the  distinction between  signed and  unsigned is
that, here we use  ANGLE merely as an internal  counter, which in its  raw state
might as well be unsigned,  but the result of the  test must be signed as  it is
the output of the oscillator.



12-4. PULSE_TRAIN

During a  pass in which  ANGLE overflows  a pulse of  +.5 (01000...,  signed) is
produced.  Otherwise zero is returned. ANGLE wraps around (as always).



12-5. COS_FM

The oscillator output is determined by the sum of FREQUENCY (GJ) and FM.  Notice
that we are  taking the sine of  the frequency term, not  the angle term,  as we
would for a normal oscillatory mode.  This is another case where terms  are used
for something  other than that  for which  they are named.  What this  mode does
basically is just look up whatever it finds in sum memory location FM in  a sine
table. The addition of the  FREQUENCY term is peripheral.  This  mode, therefore
doesn't really oscillate as such.   Its principal use is looking up  sine values
for certain filtering applications.



12-6. SUM_OF_COSINES

This allows the  addition of many equal  amplitude cosine waves together  in one
generator, rather than  having to dedicate several  to the task.  The  result of
the  sum of  many  equal amplitude  cosines  where the  frequencies  are integer
multiples  is  a  band-limited  pulse  train.  This  means  that  the  waveshape
approaches that of a pulse train,  but the frequency bandwidth is no  wider than
the difference of the frequency of the highest component minus the  frequency of
the lowest.  The frequency of the highest component of a regular pulse  train is
infinite, therefore the bandwidth is also infinite.

                                    Page 30
LRNSAM                          Oscillator modes                           DRAFT


12-7. A note on band limiting

A waveform that  has an infinite number  of partials is not  band-limited (seems
reasonable,  as tautologies  go...).   Since we  are working  in  a sampled-data
world, any partials that exceed in frequency 1/2 the sampling rate  are "folded-
over" back into the area below that limit and are included in the total spectrum
in strange and miraculous ways. The modes SQUARE, SAWTOOTH, and  PULSE_TRAIN are
all non-band limited  under all conditions.  The SUM_OF_COSINES and  COSINE mode
are band  limited but  SUM_OF_COSINES can  still cause  foldover if  its highest
frequency  component  exceeds  half  the sampling  rate.   SINE  can  also cause
foldover if its  frequency exceeds half  the sampling rate.   PULSE_TRAIN always
has terrible foldover and is not usually what you want to listen to.  SQUARE and
SAWTOOTH are  technicaly not  band limited,  but the  amplitude of  the partials
falls off rapidly enough  so that if the frequency  is not too high,  the higher
partials are not audible.


































                                    Page 31
LRNSAM                                                                     DRAFT


                                   Section 13
                                   _______ __

                              Envelope processing
                              ________ __________



On  the envelope  side, a  12 bit  unsigned fractional  binary number  (a number
between 0 to 1) is produced which is used to scale the output of  the oscillator
by  multiplication.  Since  the result  of the  oscillator side  is  signed, the
multiplication is two quadrant.



13-1. EXPONENT and RATE
EXPONENT (GQ) stores the  current value of the  scaling number.  RATE (GP)  is a
constant which is added into EXPONENT (GQ) every pass to increment  or decrement
it thus augmenting or  diminishing the amplitude scaling number.   Taken through
time therefore, EXPONENT (GQ) produces a ramp function from 0 to 1  (unless RATE
is  exactly 0,  of course,  in which  case EXPONENT  remains constant).   If the
generator is  in one  of the  "sticky" modes,  then the  EXPONENT will  not wrap
around.  It will "stick" at  the last value attained before overflow  (which may
or may not be the largest number it can represent).



13-2. ASYMPTOTE

The  value in  ASYMPTOTE (GL)  is added  to (or  subtracted from)  the  value in
EXPONENT  and this  final number  is the  one used  to scale  the output  of the
oscillator.  The  effect of ASYMPTOTE  therefore is to  offset the the  value in
EXPONENT by some constant value.

It can be cleverly used in conjunction with sticky mode as follows: Say  you are
producing a very complex amplitude function, and you want to start  the envelope
at .16 and let it increment by some value for a few passes to .95.   Well, let's
remember  the potential  for  disaster that  can  happen if  the  command that's
supposed to  catch it at  .95 doesn't  arrive in time.  It would  appear offhand
that, since the value we want to stop at is well below overflow, we don't get to
use the sticky  envelope feature.  But!   Let's put an  initial value of  .05 in
EXPONENT, and lets put .16 - .05 = .11 in ASYMPTOTE.  The number in EXPONENT now
goes  from .05  to overflow  and will  therefore stick  if necessary,  while the
actual output of the envelope side goes from .16 to .95 as usual.

If an exponential envelope curve is required, then EXPONENT (GQ) is used to look
up a value in a table  of exponents which is then returned instead  of returning
its linear value.  The  linear value used to do  the lookup is kept,  however to
compute the subsequent table address to lookup.  The value of ASYMPTOTE is added
to (subtracted from) the value from the exponential table.

                                    Page 32
LRNSAM                        Envelope processing                          DRAFT


The result of the envelope process is multiplied by the result of the oscillator
process.  This constitutes a 2 quadrant multiply.  The final result is an 18 bit
number and is added into  sum memory right adjusted, sign extended.   This final
result is  added to whatever  is the current  value in this  pass sum  memory in
location SUM_MEMORY.

Note: when the output of a  generator is being used as the  modulating frequency
of another generator, the full 20  bit sum memory word is read into  the carrier
generator's FM port and is added with the bits in FREQUENCY left  adjusted.  But
the default output of the modulating generator is 18 bits rignt adjusted.  So we
must scale the amplitude of the modulating frequency to make it be left adjusted
in the 20 bit  word in SUM MEMORY  so its high order  bit will line up  with the
high order bit in FREQUENCY so as to add properly.  This is done  by multiplying
the amplitude of the modulating generator  by 4, which has the effect  in binary
arithmetic of left-shifting the entire  number two places.  This puts  the high-
order bit of the  modulating waveform into the  20th bit position in  SUM MEMORY
and it is left justified.
































                                    Page 33
LRNSAM                                                                     DRAFT


                                   Section 14
                                   _______ __

                                 Envelope modes
                                 ________ _____



These modes specify  the arithmetic operations  of envelope processing.   How we
use the "Definition" terms will be explaned when we talk about  actually running
the box.  Bascally, it consists of a predefined word full of bits which  acts as
a flag to the box to enable some mode.

Definition Function
--------------------------------------------------------------------------------
LPLUSQ     Add EXPONENT (GQ) to the offset constant
           in ASYMPTOTE (GL).
LMINUSQ    Subtract EXPONENT (GQ) from offset constant
           in ASYMPTOTE(GL)
                                  -EXPONENT
LEXPLUS    Add ASYMPTOTE (GL) to 2          and scale.
                                         -EXPONENT
LEXMINUS   Subtract ASYMPTOTE (GL) from 2          and scale.

                                     Fig. 7
                                     ____ _


LPLUSQ: The value in EXPONENT is added to the constant in ASYMPTOTE.

LMINUSQ: The constant value in ASYMPTOTE (GL) is subtracted from EXPONENT (GQ).

LEXPLUS: The value in EXPONENT is  used as a lookup in an exponential  table and
the result is  added to the constant  in ASYMPTOTE.  The exponent  returned from
                                    -EXPONENT*16
the table lookup can be defined as 2            .  To see what's happening here,
first remember that  EXPONENT takes values  between 0 and  1.  If EXPONENT  is 0
                                          0
then the value from the table will be 1 (2  = 1).  If EXPONENT is 1/16, then the
                                                 -16
result is .5.  If EXPONENT is  1, the result is 2   , taken as  zero.  Inbetween
values are exponential.  We  could also look at  it in binary. The  following is
all unsigned.









                                    Page 34
LRNSAM                           Envelope modes                            DRAFT


EXPONENT:              Result from table:
binary       decimal   binary              decimal
0000 0000... 0         111 111 111 111     1
0001 0000... 1/16      011 111 111 111     1/2
0010 0000... 1/8       001 111 111 111     1/4
0011 0000...           001 111 111 111     1/8
0100 0000... 1/4       000 111 111 111     1/16
 .
 .
 .
1111 1111... 1         000 000 000 000...  0

So in the  binary representation, it  is like the EXPONENT  has a 4  bit integer
part (the high order bits) and an 8 bit fractional part(the rest of the word).

LEXMINUS: Exponential  table lookup is  done, and value  is subtracted  from the
constant.
































                                    Page 35
LRNSAM                                                                     DRAFT


                                   Section 15
                                   _______ __

                           Shape-shifting generators
                           ______________ __________



As you know, the generator parameters must do double duty sometimes depending on
whether they are acting as data paths or waveform generators.  Here we  get into
the guts of the issue.(1)

The  generators function  in a  variety  of ways  in the  box.   Their principle
functions are 1) waveform production, 2) the transfer of data from sum memory to
CPU, 3) the  transfer of data from  CPU to sum memory,  4) the transfer  of data
from sum  memory to  the DACs and  5) doing  sine table lookups  on data  in sum
memory.  All of this is  done, without having to create new  processing elements
and parameters, by making various existing generator parameters function in more
than one way.   Each generator run mode  uses different parameters  in different
ways, hence the following "road map" which gives a close (though not definitive)
look at them.

Remember that the generators have two parts, an oscillator part and  an envelope
part.   The oscillator  side of  the generator  is running  if it  is in  any of
G_WAIT, A_RUNNING, B_RUNNING C_RUNNING  and READ_DATA modes.  It is  not running
in INACTIVE, G_PAUSE, WRITE_DATA and WRITE_DAC modes.  By "running" I technicaly
mean whether ANGLE is being updated or not.  A special exception to this rule is
COS_FM where part of the oscillator is running (the sine table lookup  part) but
the rest of it is not.

So here is a  list of the generator parameters,  and a description of  what part
they play in the various run modes.



15-1. SWEEP

SWEEP (GO) is the oscillator frequency sweep rate.  SWEEP stores a number which,
when the oscillator is running, is  added to FREQUENCY (GJ) on every  pass, This
allows FREQUENCY to rise or fall at a given rate.  SWEEP functons as such in all
oscillator run modes.  If the generator is in WRITE_DAC mode, then SWEEP is used
to store the address of the DAC, and its value is not added into FREQUENCY.




_______________________
(1) Note, this  is not the  order of the  processing steps.  For  the definitive
processing  order,  as  well  as  all  the  gory  details,  see  the Programming
Specification.

                                    Page 36
LRNSAM                     Shape-shifting generators                       DRAFT


15-2. FREQUENCY

FREQUENCY  (GJ)  stores the  oscillator  frequency.  The  data  from  sum memory
addressed by FM is added to the value in FREQUENCY in all oscillator  modes.  FM
will  presumably  address  either  frequency  modulation  data  from  some other
generator or  will have data  to be sent  to a DAC.   If the oscillator  side is
running  then  RATE  is  also  added  to  FREQUENCY  (as  described  above, this
increments the frequency by a constant, for frequency slewing, or glissando).



15-3. ANGLE

ANGLE (GK).  The primary function of  ANGLE is to store the current  position of
the oscillator in the cycle of  the wave it is producing, called  the oscillator
angle.  This term is used as an index to lookup sine values on a table or as the
datum of a logic test for the straight line waveforms.  However, it is used this
way only when the oscillator is running.

The principal exception to this is COS_FM.  In COS_FM mode, ANGLE is not used at
all. Instead, the output of the oscillator is the sine of the sum of FREQUENCY +
<contents of FM>.

If the generator run mode is  WRITE_DATA or WRITE_DAC then the data sent  to the
CPU is the word addressed by  FM.(1).  The entire table is actually  shifted one
half a position, as  it were.  This is  because the CSC taken  in SUM_OF_COSINES
mode is 1/SIN, and if we took  the sin of 0 we'd get infinity.  But  by shifting
the sine table, this is mitigated.  This shift won't screw you until you  try to
do sine summation synthesis.]



15-4. NCOSINES and SCALE

N_COSINES (GN), SCALE  (GM).  These are  relevant only for  SUM_OF_COSINES mode.
N_COSINES specifies  the number of  cosines to be  summed.  SCALE gives  a scale
factor for normalizing the summed result.  (If you have a bunch of cosine waves,
remembering that they all have an amplitude of 1 at zero degrees, their  sum can
begin  to  stack   up,  hence  the   need  for  normalization.)   Actually,  the
SUM_OF_COSINES algorithm should be normalized by 1/NCOSINES, but we don't have a
divider at that point in the processor, so we approximate it by just scaling the
result of the cosine summation formula by shifting the bits in the word right or
left, which you recall is the same  as dividing or multiplying by a power  of 2.
This gets us within a factor of 2 of the correct normalization, not perfect, but
acceptable.
_______________________
                                                                        -13
(1) Note that in the sine table there is an implicit phase shift of 2π*2

                                    Page 37
LRNSAM                     Shape-shifting generators                       DRAFT


Please note that  SCALE should be  set to zero when  the oscillator mode  is not
SUM_OF_COSINES, since a  non-zero value in SCALE  implies a subtraction  of one.
(This is necessary for the SUM_OF_COSINES algorithm to get rid of the cos(theta)
term).  Note that this also  means that in SUM_OF_COSINES mode, SCALE  should be
non-zero.(1)



15-5. FM

FM (GFM) has  the address of  a location in sum  memory which the  generator can
read.  FM can read from either generator or modifier last pass.  FM is  read and
its value is added together with FREQUENCY in all oscillator modes.  Please note
that if you don't want data from sum memory then you must give FM the address of
some location in sum memory which is never written into and will hence always be
zero.



15-6. SUM_MEMORY

SUM_MEMORY (GSUM) has  the address of  a location in  sum memory into  which the
generator writes  its output.  This  output is generally  added to  the contents
already there from this pass.  But if the run mode is READ_DATA, then sum memory
gets the final output of the generator PLUS the data read from the main computer
CPU.  This REPLACES the contents of the sum memory location addressed (so its no
longer really sum memory).



15-7. RATE EXPONENT and ASYMPTOTE

RATE  (GP),  EXPONENT  (GQ)  and  ASYMPTOTE  (GL)  are  relevant  only  when the
oscillator is in a wave producing mode and the envelope is running as  well. The
envelope side is running  in A_RUNNING, B_RUNNING and C_RUNNING  only.  EXPONENT
is  the  envelope  increment  corresponding  in  function  to  FREQUENCY  in the
oscillator.  It  can be  augmented or  diminished by  a number  in RATE  just as
FREQUENCY can be changed by SWEEP.  EXPONENT is either used directly as a linear
function or  as an  index to an  exponential table  used to  produce exponential
envelopes.   The  resulting  number  returned  from  either  path  is  added  to
ASYMPTOTE, which is a constant used to offset the envelope ramp from the X axis.



_______________________
(1) For  a discussion of  sine summation synthesis  see Godfrey  Winham, Kenneth
Steiglitz, "Input Generators for Digital Sound Synthesis", J. Acoust.  Soc. Am.,
                                                           __ _______  ____ ___
Volume 47, #2 (part 2) 1970, pp665-666.

                                    Page 38
LRNSAM                                                                     DRAFT


                                   Section 16
                                   _______ __

                                 Magic numbers
                                 _____ _______



Specifying frequency and duration values to the box needs a  little explanation:
We know that MUSCMP requires scaling  by a value called MAG.  (MAG is  the ratio
of  the waveform  table length  to the  clock rate,  or in  a  typical instance:
512/12800 = .04). For instance, if a variable P3 is used to scale  the frequency
of an oscillator and it has  a frequency value in Hertz, the scaled  number sent
to the MUSCMP oscillator would be MAG*P3.

In the box, FREQUENCY (GJ),  the oscillator frequency parameters must  be scaled
similarly to the way the frequency in MUSCMP is scaled by MAG.  The magic number
                                        28
which corresponds to MAG in the box is 2  /sampling_rate.

To see this, remember that FREQUENCY (which is 28 bits long) is really  a number
that is added into the number  in ANGLE every pass.  As ANGLE goes  from 0000...
to 1111...  we go through a  complete cycle of our waveform.  Consider  the case
where we have all ones in  FREQUENCY.  What happens to ANGLE every pass?   It is
wrapped  around exactly  back to  where it  was last  time.  Thus  the frequency
produced is  logically equal  to the sampling  rate since  ANGLE goes  through a
complete revolution every pass. (Though in fact the samples that are  seen don't
reflect  this, since  ANGLE always  wraps around  to the  same place  they never
change value.) Similarly, if FREQUENCY  contains all zeros, the number  in ANGLE
will not change, and the frequency of the generator is equal to zero  (though by
inspection the values produced  are indistinguishable from the above  case where
the frequency is equal to the sampling rate).  This means that if the  number in
FREQUENCY is 100000... (unsigned), then  we are producing a frequency of  1/2 of
the sampling rate, 010000... is 1/4 of the sampling rate, &etc.  So you  can see
that the frequency of a generator is a function of the speed at  which FREQUENCY
makes ANGLE go through a complete  cycle, and this in turn is controlled  by how
many passes are accomplished (and therefore samples produced) per second.

For a concrete example, say we had a sampling rate of 12800 samples  per second.
                                 28           28   7         21
The magic number  would then be 2  /12800  = 2  /(2 *100) = 2  /100  = 20971.52.
This number multiplied by some  frequency will put the correct binary  number in
                                                             6
FREQUENCY.  For instance, 1/2 the sampling rate = 6400 Hz = 2 *100.

                    21
                   2
         6                 27
        2 *100 * ------ = 2  ,                                            Equ. 4
                   100

                                    Page 39
LRNSAM                           Magic numbers                             DRAFT


which in a 28 bit binary word is 10000... as in our example above.

If you were really awake right  now you might be wondering how, if  FREQUENCY is
all ones,  that it turns  ANGLE over  exactly once since  FREQUENCY has  28 bits
whereas ANGLE has only 20?  It  should, by rights, turn it over 8  bits farther,
no?  It's like this: the low order eight bits (the rightmost bits)  of FREQUENCY
are not added into ANGLE at all, rather, the high order 20 bits of FREQUENCY are
all that are added to ANGLE.  So what good are the low order 8 bits?   The point
is that we  want to try to  get as fine a  change of frequency as  possible when
FREQUENCY is being incremented by SWEEP so that we can represent  extremely slow
changes  in  frequency.   To  this end,  SWEEP  is  added  into  FREQUENCY right
adjusted, meaning its 20 bits are added to the righthand, or low order,  20 bits
of FREQUENCY.  But since only the left  20 bits of FREQUENCY are used to  add to
ANGLE,  the  right  eight bits  act  as  an accumulator  of  subtler  changes of
frequency than can be represented by  20 bits.  When the eight bits  finally sum
up enough to toggle  the 9th bit, then the  amount is registered as part  of the
new 20 bit frequency  increment sent to ANGLE.  Thus  we get eight more  bits of
frequency  precision with  respect to  glissando, without  having to  go  to the
expense of making ANGLE lookup in a 28 bit sine table.

One further word of interest: FREQUENCY  is a 28 bit word, but the  largest word
that can be loaded in one update  tick is 20 bits.  So Samson has given  you the
option of loading just 20  bits or all 28 by  taking two update ticks to  do it.
All this is done automatically by the software.  If the number you want  sent is
less than or equal to 20 bits it loads only that many.  But if this is the case,
then FREQUENCY acts like  a 20 bit word (the  sign bit is extended).   The magic
                     20
number then becomes 2  /clock_rate.

All processing that results in output  is in terms of passes.  This is  the same
as MUSCMP, where each output sample is derived from one "pass" through  the code
which comprises the instrument.  One  difference is the treatment in  scaling of
the  duration  of envelopes.   In  MUSCMP, envelopes  are  usually  generated by
oscillators, which means that if a  variable P2 has a value of absolute  time in
it, the scaled number  sent to the oscillator  would be MAG/P2.  However  in the
box, the scaling is simpler.  Since the envelope side of the  generators returns
a new envelope value on every pass, we only need to let the envelope run as many
passes as it would take to get  the value we want: so the number of  passes that
the  envelope would  be allowed  to process  would be  P2*clock_rate.   We would
increment the size of the envelope by N/P2*clock_rate on every pass to  obtain a
final value of N at the end of P2*clock_rate passes.

Another  useful  formula is  for  SWEEP, the  frequency  sweep rate.   To  get a
frequency sweep from Frequency_1  to Frequency_2 in one second,  put Frequency_1
times  the  box's  magic  number  in  FREQUENCY  (GJ),  and  the  difference  of
                                                       28           2
Frequency_2 - Frequency_1 times another magic number, 2  /clock_rate , in SWEEP.
That would be:

                                    Page 40
LRNSAM                           Magic numbers                             DRAFT


                         28
FREQUENCY ← Frequency_1*2  /clock_rate,                                   Equ. 5

and

                                     28           2
SWEEP ← (Frequency_2 - Frequency_1)*2  /clock_rate .                      Equ. 6

The derivation of the formula for SWEEP is as follows:
                     28                             28
       (Frequency_2*2  /clock_rate) - (Frequency_1*2  /clock_rate)
SWEEP ← ------------------------------------------------------,
                                clock_rate



                                         28
            (Frequency_2 - Frequency_1)*2  /clock_rate
SWEEP ←     ---------------------------------------------,
                            clock_rate



                                       28
          (Frequency_2 - Frequency_1)*2  *1/clock_rate
SWEEP ←   -----------------------------------------------,
                            clock_rate



                                     28
SWEEP ← (Frequency_2 - Frequency_1)*2  *1/clock_rate*1/clock_rate,



                                     28           2
SWEEP ← (Frequency_2 - Frequency_1)*2  /clock_rate .                      Equ. 7



To make this sweep happen in some other than one second, simply divide the above
number by the desired duration.







                                    Page 41
LRNSAM                                                                     DRAFT


                                   Section 17
                                   _______ __

                                   Modifiers
                                   _________





17-1. Modifier parameters

Modifiers do all kinds of things.  Again, what you have them do determines which
of the following terms are relevant.  They have the following locations:

Id    Size Definition          Function
--------------------------------------------------------------------------------
M0    30   COEFF0              coefficient
M1    30   COEFF1              other coefficient
L0    20   TERM_0              running term
L1    20   TERM_1              other running term
MIN   8    A_IN                read address in sum memory
MRM   8    B_IN                other read address
MSUM  7    SUM_MEMORY          write address in
                               sum memory
           ADD_SUM_MEMORY      same as SUM_MEMORY.
           REPLACE_SUM_MEMORY  also has write addr.
                               but result replaces sum mem. value
MMODE 9    MODE                run mode of modifier
                               and scale factor for the multiplies
              A_SCALE          scales COEFF1. (That's right A↔1!?!)
              B_SCALE          scales COEFF0.
FUNCTION                        same as MODE.

                                     Fig. 8
                                     ____ _




17-2. Running terms

TERM_0  and  TERM_1  are  "temporary"  locations  dedicated  to  that particular
modifier which will have as their contents whatever was last stuffed  into them.
The machine never resets them to zero as it does to sum memory.  The main use of
the  running  terms is  in  certain  of the  modifier  procedures  which require
information to be carried over from the calculations performed on the last pass.
Filtering is an example of this, as is random number generation.   These running
terms are also used when there is not enough time during one tick to do  all the
computations, so a temporary location is needed to hold the partial results over


                                    Page 42
LRNSAM                             Modifiers                               DRAFT


to the  next pass.   The running  terms live  inside their  particular modifier.
They are not cleared on every pass as is sum memory.  They always have  the last
value that was written into them,  and they divulge it only to  their associated
modifiers. You can, of  course initialize them by  using an update tick  to load
them, which you would want to do, for instance, for random number generaton.



17-3. Coefficient terms and Read terms

COEFF0 AND  COEFF1 retain  the number  deposited in  them across  passes thereby
acting as constants.  Their usual function is to scale the running terms  or the
read terms, A_IN and  B_IN.  The read terms,  as in the generators,  contain the
address of a location in sum memory, and all arithmetic operations with them act
on the value of the number in sum memory that they address.



17-4. Scaling terms

The product of a coefficient term by either a running term or a read term can be
scaled by  A_SCALE and B_SCALE  to make it  bigger.  Making the  product smaller
                                                                         _______
than one is  simple since normally the  multiplies will always be  in fractional
                          ________
range (-1.0000... to +.9999...).  (The exception is integer mixing, discussed on
page 46) Scaling is the only way to make the result of the multiply  bigger than
one.  Scaling is done by left-shifting (LSH) the multiplicand by one,  two three
or  four places.   The  effect this  has is  to  raise the  multiplicand  by the
following respective powers  of 2: 1,  2, 4, 8.  To  see this, take  binary 0010
which is decimal 2, and LSH it by 2 producing 1000 = 8.  Notice that the scaling
terms are put under MODE.  It worked out that there was enough room left  in the
MODE command to accommodate this extra baggage.  So you set the SCALE terms with
the power of 2 you  want, and that information is  tacked on to the rest  of the
MODE command and shipped  off to the box.   Please note that A_SCALE  scales any
multiplication done  with COEFF1, while  B_SCALE scales any  multiplication done
                               _
with COEFF0.
          _



17-5. Mode

This defines the  algorithm that the modifier  will use.  An alternate  word for
this  is FUNCTION  since for  modifiers that  we are  selecting is  a particular
function or algorithm or process, whereas the word MODE might better be used for
setting the mode of  an oscillator.  But the  two terms are equivalent,  and the
SAIL  procedures  that handle  them  know whether  the  MODE selected  is  for a
modifier  or generator.   For a  complete account  of the  various  flavors, see
section 18.


                                    Page 43
LRNSAM                             Modifiers                               DRAFT


17-6. Sum memory

Called the  same as  in the  generators, but  the assembler  knows which  one is
specified  by  context.  There  are  three  terms:  SUM_MEMORY  is  the  same as
ADD_SUM_MEMORY and for  both, the result of  the modifier procedure is  added to
the preexisting  contents (if any)  in the sum  memory location  addressed.  But
modifiers also can have their output replace the contents of sum  memory, hence:
REPLACE_SUM_MEMORY.



17-7. Initializing

It is important to note that  the first time you call a modifier  procedure that
uses  any of  these terms,  especially the  running terms,  they will  have some
leftover numbers in them that will not be meaningful in terms of your  new data,
but that  will no  doubt affect  how that  data is  subsequently handled  by the
modifier.  Therefore it is first necessary to initialize all of  these locations
to  some meaningful  value.  This  goes  as well  for the  generators  and delay
memory.





























                                    Page 44
LRNSAM                                                                     DRAFT


                                   Section 18
                                   _______ __

                              Modifier procedures
                              ________ __________



The name of the process is followed by its reserved definition followed  in turn
by an explanation.  Unless otherwise stated, the computation is in 20  bit two's
compliment, fixed binary  point notation.  Fixed  binary point is  a "fractional
notation" in that it represents only fractional numbers.  Decimal representation
of the binary range is from -1.000... to +.999...



18-1. Inactive

M_INACTIVE

Dead  to the  world.  As  with  generators, being  inactive does  not  clear its
memory.  The  parameters (COEFF0 and  COEFF1 and such)  will be set  to whatever
they were  before the  modifier went inactive.   It will  also still  occupy its
place in the ordered processing of modifiers and use up two ticks.



18-2. Mixing

MIXING

Result ← COEFF0*A_IN + COEFF1*B_IN.

Signals in A_IN  and B_IN are multiplied  by COEFF0 and COEFF1  respectively and
added together.   The COEFF terms  act here  and in many  other of  the modifier
procedures as gain factors for the weighting of the respective input terms.  The
result is placed back in sum memory location referenced by MSUM.  A_IN  and B_IN
are locations which contain addresses of numbers in sum memory whose  values are
looked up for these calculations.  A_IN and B_IN will usually be used  to lookup
sum memory locatons which  will usually contain current instantanious  values of
waveforms being created by generators (or whatever).  A_IN and B_IN can  also be
values that are the  result of modifier processing  which are then picked  up by
other modifiers, etc.  The  multiplications are in fractional form  as discribed
above.







                                    Page 45
LRNSAM                        Modifier procedures                          DRAFT


18-3. Integer mixing

INT_MIXING

Result(integer) ← COEFF0*A_IN + COEFF1*B_IN.

Computation  is the  same as  for fractional  mixing above  except  that integer
multiplying  is done.   The difference  is as  follows: In  fractional notation,
since  all numbers  are considered  to be  between .999...   and  -1.000.... the
result of  a fractional multiplication  will never be  greater than  this range.
That is, if you multiply the largest fixed binary point number you can represent
by another  one equally large,  the result  is still that  number.  There  is no
overflow.  For  instance, if you  have a  number 5 bits  long, and  you multiply
fixed binary  point numbers .11111  by .11111 you  get .11111.  However,  if you
multiply binary integer 00010.  by 00010.   you get 00100.  You can see  that if
you kept this up, you would get a result that is larger and  larger: 00100*00100
=  10000,  etc.   (Eventually  you would  get  overflow,  which  means  that the
significant bits are to the left  of the left-most place for them and  the value
is therefore greater than the space available for it.) So, now that  you've read
all this,  the point  is only  that integer  mixing allows  you to  make numbers
bigger while multiplying, while in fractional mixing above, the numbers only get
smaller.  The other way to do this is of course to use the SCALE terms discussed
above.



18-4. Latch

LATCH

Result ← TERM_1.  If COEFF1*B_IN ≠ 0 then TERM_1←A_IN.

The modifier gives as its result whatever was in TERM_1 from last pass.  Then it
looks to  see if it  should sample a  new value.  If  COEFF1*B_IN is not  0, the
condition is then  true and it  looks up the value  of A_IN and  replaces TERM_1
with it.   Otherwise, TERM_1 hangs  around until next  time.  The point  of this
function  is to  wait  for a  triggering condition  to  be true,  then  sample a
changing variable being stored in sum memory (like to look at a sine  wave being
fed into sum  memory) and then  to return the  value sampled.  If  the condition
becomes not true then  the value of the  last sample taken before  the condition
became false  is returned.   This value  will be  returned repeatedly  until the
condition is true again at which  time a new sample will be taken  and returned.
(Since a running term is used, it is necessary to clear it first.)






                                    Page 46
LRNSAM                        Modifier procedures                          DRAFT


18-5. Signum

SIGNUM

If COEFF0*A_IN < COEFF1*B_IN then the result ← -1 (integer)
else if the multiplicands are equal then the result ← 0
else if COEFF0*A_IN > COEFF1*B_IN then the result ← 1 (integer).

This tests the sign of the expression.  It is similar to the  Fortran "numerical
if" statement in that it allows one of three subsequent forks to be taken as the
result of an expression being >, =, or < 0.  The result is in integer form.



18-6. Zero-crossing pulser

ZERO_CROSSING_PULSER

If (B_IN*COEFF0)*(TERM_1*COEFF1) ≤ 0 then result ← - 1
(all bits on) else result ← 0. TERM_1 ← B_IN*COEFF0.

This  one figures  out waveform  zero crossings.   It looks  up  two consecutive
points in the waveform time  domain and asks whether their product  is negative.
That is, if you have two samples  in a waveform where the first is  negative and
the second  is positive, or  vice versa, your  waveform must have  crossed zero.
Hence the two samples surrounding  a waveform zero crossing will  be represented
by numbers of  alternate signs.  The product  of such numbers is  negative while
the  product of  numbers of  homogenous  sign is  positive.  If  the  product is
negative (a zero crossing was detected)  a -1 (all bits on) is returned,  else 0
(all  bits off).   (Another  possibility is  if one  of  the two  values  of the
waveform is 0 e.g. if values were 1 and 0.  The product would be 0, so  there is
a trap for this also.)



18-7. Minimum

MINIMUM The lesser of A_IN*COEFF0 or B_IN*COEFF1 is returned.



18-8. Maximum

MAXIMUM The greater of A_IN*COEFF0 or B_IN*COEFF1 is returned.





                                    Page 47
LRNSAM                        Modifier procedures                          DRAFT


18-9. Amplitude modulation

AMPLITUDE_MODULATION

Result ← TERM_1*COEFF1.  TERM_1 ← A_IN*((B_IN+1)/2).

This basically multiplies two waveforms together such that B_IN is used to scale
the amplitude of A_IN.  This is  done by multiplying the number to be  scaled by
another which has values between 0 and 1.  If this scaling is done through time,
the overall result  is of the amplitude  of A_IN being dynamically  modulated by
B_IN.  To do this, we first need to scrunch B_IN so its value lies between 0 and
1.  But  B_IN is  a binary  point number  which has  values between  .999... and
-1.000...  So to convert B_IN, first add 1 to B_IN, to make its range go  from 0
to 2.  Then divide  that by 2 to  make it go between  0 and 1.  Now  we multiply
A_IN*B_IN and return the product.   This process is repeated for every  point on
the waveforms A_IN and B_IN.  The steps the box takes to do this are as follows.
The value TERM_1*COEFF1  is returned where TERM_1  was determined the  last time
around  to  be  A_IN*((B_IN+1)/2).   This  process  constitutes  a  two quadrant
multiply in that the sign of A_IN is significant but that of B_IN is  not (B_IN,
being scaled between 0  and 1 will always be  positive).  So, if we  plotted the
point (A_IN,B_IN) on a graph, any  product of A_IN and B_IN would only  need two
quadrants to be represented.



18-10. Four-quadrant-multiply

FOUR_QUAD_MULTIPLY

Result ← TERM_1*COEFF1.  TERM_1 ← A_IN*B_IN.

TERM_1 scaled by COEFF1 is returned.  A new TERM_1 is then computed as A_IN*B_IN
and stored in TERM_1  to be multiplied by  COEFF1 next time around.   Here, both
the signs of A_IN and B_IN are  significant, and the product could be in  any of
the four Cartesian quadrants.  For synthesizer people, this is the ultimate ring
modulator.



18-11. Uniform noise

U_NOISE

TERM_1 ← Result ← COEFF0*TERM_1 + TERM_0.

We know that if we multiply integers, sooner or later, if we don't stop, we will
overflow, and the significant digits or bits of the product will be lost off the


                                    Page 48
LRNSAM                        Modifier procedures                          DRAFT


left edge  of our remaining  number.  That leaves  only insignificant  ones!  So
these low order, insignificant bits are then returned as a pseudo-random number.
These numbers taken over a period of time produce white, or uniform  noise.  The
procedure adds TERM_0  to the integer product  of TERM_1*COEFF0 and  returns the
result.   Overflow  is purposefully  ignored.   It overwrites  TERM_1  with this
result, to be used  next time around.  The one  tricky aspect of this  method is
that the numbers used  to prime the procedure must  be chosen with care  in this
system. Andy Moorer has the following remarks about the process:

        To make a modifier make pseudo-random numbers, you have  to choose
      COEFF0  (the  multiplier),  TERM_0  (additive  constant)  and TERM_1
      (starting random number).  Knuth(1) has a whole chapter on this sort
      of thing.   Probably the  best you can  do for  these things  is the
      following:

        1) COEFF0 is 30 bits long, but only the upper 20 bits are  used in
      the multiply. This being the case, it can be a 20 bit  number chosen
      essentially at random, but it ought to have some middle bits  on and
      it ought to  end in 101 (for  maximum potency). An example  is this:
      00000100110100100101   (octal   46445).    (Note   that   since  the
      coefficient is really 30 bits long, when we pass the number  as full
      word data we have to stick  ten more zeros on, which gives  us octal
      115112000.)

        2) This being the case,  TERM_0 should be set to zero  (unless you
      know  what you  are  doing). Setting  it non-zero  doesn't  make the
      number any more "random".

        3) The starting random number should be set to something non-zero,
      like the date  or time, your telephone  number, pi, or  whatever. It
      should be a whole word full of bits.



18-12. Triggered uniform noise

TR_U_NOISE

Result ← COEFF0*TERM_1 + TERM_0.  If B_IN*COEFF1 ≠ 0 then
TERM_1←result.

A new noise  value is calculated  as in U_NOISE  above only when  the "trigger",
B_IN*COEFF1,  is  not 0.   This  allows the  production  of noise  values  to be
dependent on some  state in the  box rather than requiring  a command to  get it
going or stoped, thereby lightening the command stream.  Priming of the terms of
this procedure should be done according to the rules for U_NOISE above.
_______________________
(1) Knuth, The Art of Computer Programming, Vol. II.
           ___ ___ __ ________ ___________

                                    Page 49
LRNSAM                        Modifier procedures                          DRAFT


18-13. Threshold

THRESHOLD

If COEFF0*A_IN + TERM_0 < 0 then result ← 0 else if ≥ 0 then result ←
COEFF1*B_IN.

This allows  one to  detect when a  signal crosses  some threshold.   The tested
value is A_IN*COEFF0 + TERM_0.  If it is less than 0 then 0 is returned, else if
it is greater than or equal to 0 then B_IN*COEFF1 is returned.   B_IN*COEFF1 has
nothing to  do with  the value  of the tested  expression but  could be  made to
evaluate to a  constant acting as  a trigger to some  other process or  since we
have B_IN in the  term it could be some  other waveform which would  then become
the output of the modifier.



18-14. Invoke delay unit

INVOKE_DELAY_UNIT

(B_IN(low order 5 bits) ← Unit#.)
result ← TERM_0 + TERM_1*COEFF0;
TERM_0 ← <delay_memory_value from delay unit>;
TEMP0 ← A_IN + <delay_memory_value>*COEFF1;
(<wait for next pass>)
TERM_1 ← TEMP0;
(<wait for next pass>)
<value sent to delay unit> ← TEMP0;

Delay units are  called from modifiers.  The  delay units are discussed  on page
61. The modifier acts like a processor for the delay units, and the  delay units
only do the clerical work of stuffing and retrieving data.  The  processing that
the modifier does to the data sent to the delay units is as follows:

The address of the delay unit to  be accessed this pass is fetched from  the sum
memory location addressed by B_IN.  At this point, TERM_0 has the datum that was
read from delay memory three passes ago.  This is added to the product of TERM_1
and COEFF0.  TERM_1 has the datum that was read from sum memory two  passes ago.
(If you don't want this, just set COEFF0 to zero, of course.) The result of this
sum is then returned to the sum memory location addressed by SUM_MEMORY.

Next we receive a new delay memory  datum from the delay unit and deposit  it in
TERM_0.

Then we  process the data  to be  written to delay  memory.  This  takes several
passes, therefore we  must use TEMP0 which  is an internal register  accessed by


                                    Page 50
LRNSAM                        Modifier procedures                          DRAFT


the modifier that is not cleared from pass to pass.  TEMP0 is given the  sum of:
1) a word from sum memory addressed by A_IN (presumably the next sound sample to
be reverberated, etc.) and 2) the  product of the current datum from  sum memory
and COEFF1.  This takes a pass.  On  the next pass TERM_1 is given the  value in
TEMP0. On the next pass TEMP0 is passed to the delay unit to be deposited in its
memory.











































                                    Page 51
LRNSAM                                                                     DRAFT


                                   Section 19
                                   _______ __

                              Filtering algorithms
                              _________ __________



Various  flavors  of  filtering  are available.   Here  is  the  barest outline,
starting with a SAILish version of the code performed.  Section 19 has  a little
more on how to use them, written by Andy Moorer.



19-1. One pole

Called as ONE_POLE mode to a modifier.  Sam does the following loop:

    LOOP: Result[k]←COEFF1*TERM_1 + COEFF0*B_IN;
    TERM_1←Result[k];
    <increment k and go to LOOP>.

Or, the formula could be given as:

    Result[k]←COEFF1*Result[k-1] + COEFF0*B_IN[k],


where k  is the number  of the current  sample, B_IN[k] is  the location  of the
current input sample, COEFF0 and COEFF1 are gain factors.  The result  is formed
of TERM_1*COEFF1  + B_IN*COEFF0, then  TERM_1 is replaced  by the result,  to be
processed on the next pass.  In this way TERM_1 becomes the k-1'th sample.




19-2. One zero

ONE_ZERO

Result←TERM_1*COEFF1     +      A_IN*COEFF0,     then      TERM_1←A_IN.      Or:
Result[k]←A_IN[k]*COEFF0 + COEFF1*A_IN[k-1].










                                    Page 52
LRNSAM                        Filtering algorithms                         DRAFT





19-3. Two poles

TWO_POLES

    LOOP: Result[k]←COEFF1*TERM_0 + COEFF0*TERM_1 + A_IN;
    TERM_0←TERM_1;
    TERM_1←Result[k];
    <increment k and go to LOOP>.
This is like saying
    Result[k]←COEFF0*Result[k-1] + COEFF1*Result[k-2] + A_IN.



19-4. Two zeros

TWO_ZEROS

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←A_IN;   <increment   k  and   go   to  LOOP>.    This   is   like  saying
Result[k]←A_IN[k] + COEFF0*A_IN[k-1] + COEFF1*A_IN[k-2].




19-5. Two poles COEFF0 variable

TWO_0POLES

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←Result[k]; COEFF0←COEFF0 + B_IN; <increment k and go to LOOP>.




19-6. Two poles COEFF1 variable

TWO_1POLES

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←Result[k]; COEFF1←COEFF1 + B_IN; <increment k and go to LOOP>.






                                    Page 53
LRNSAM                        Filtering algorithms                         DRAFT


19-7. Two zeros COEFF0 variable

TWO_0ZEROS

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←A_IN; COEFF0←COEFF0+B_IN; <increment k and go to LOOP>.



19-8. Two zeros COEFF1 variable

TWO_1ZEROS

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←A_IN; COEFF1←COEFF1+B_IN; <increment k and go to LOOP>.



19-9. A little digital filtering theory

(...never hurt anybody).  Now  I bet you want to  know how to use  these things,
right? Well,  filters are  entirely specified by  their frequency  responses, so
let's take a look:  In what follows, w is  the radian frequency (2πf) of  a pure
sinusoid applied  to the filter  to determine its  frequency response. T  is the
sampling interval, or 1/clock_rate.

                                     2
ONE POLE: H(w) = COEFF0/sqrt(1+COEFF1 -2*COEFF1*cos(wT))                  Equ. 8
                            2       2
ONE ZERO: H(w) = sqrt(COEFF0 +COEFF1 +2*COEFF0*COEFF1*cos(wT))            Equ. 9
                                2
     or   H(w) = COEFF0*sqrt(1+R +2*R*cos(wT)) where R=COEFF1/COEFF0     Equ. 10
                                                          2
TWO POLES:H(w) = 1/sqrt((1-COEFF0*cos(wT)-COEFF1*cos(2wT))
                                                         2
                        +(COEFF0*sin(wT)+COEFF1*sin(2wT)) )              Equ. 11
                                                        2
TWO ZEROS:H(w) = sqrt((1+COEFF0*cos(wT)+COEFF1*cos(2wT))
                                                         2
                        +(COEFF0*sin(wT)+COEFF1*sin(2wT)) )              Equ. 12

These expressions won't mean much  without some explaination. Let's look  at the
ONE POLE  case. Since cos(0)=1,  H(0)=COEFF0/|(1-COEFF1)|. At half  the sampling
rate, wT=π so cos(wT)=-1, thus H(2πclock_rate/2)=COEFF0/|(1+COEFF1)|. So  we see
that the frequency response starts at H(0) and varies roughly  cosinusoidally to
H(2πclock_rate/2). If COEFF1<1, then  this means that for positive  COEFF1, this
is  a low-pass  filter (accentuates  frequencies around  zero) and  for negative


                                    Page 54
LRNSAM                        Filtering algorithms                         DRAFT


COEFF1, it is a high-pass filter (depresses frequencies around zero).  Note that
if COEFF1=1  or -1, you  get a filter  the frequency response  of which  goes to
infinity either at 0 or 2πclock_rate/2. This is not recommended.

For ONE  ZERO, it is  the same  deal except that  it is the  ratio of  COEFF1 to
COEFF0 that  sets the  frequency response  and the  sign is  inverted.  Positive
COEFF1 makes a high-pass and negative COEFF1 makes a low-pass.

TWO POLES: Now the really interesting (and hard to explain) stuff comes  when we
get to two of everything.  (double your pleasure, double your fun!...)  The only
way to deal with a  two pole or two zero filter  is to put it in  canonical form
(that word again!). Here is the canonical form for the TWO POLES case:

                                    2          2
    H(w) = G/sqrt((1-2*R*C*cos(wT)+R *cos(2wT)) +(-2*R*C*sin(wT)
       2          2
    + R *sin(2wT)) )                                                     Equ. 13

                                      2
where G=1, COEFF0=2*R*C, and COEFF1=-R .

Without further explaination, this is a resonator (formant filter). Its resonant
frequency is determined by C,  which is cos(2πfT). Its bandwidth  (sharpness) is
determined by R which must be less than one for stability. Typical  values would
be R=.95 for a fairly sharp filter and R=.999 for a filter that is so sharp that
it will "sing" at its resonant frequency almost by itself. To use such a filter,
you  choose the  resonant frequency,  f, choose  the bandwidth,  R,  the compute
                                  2
COEFF0=2*R*cos(2πfT) and COEFF1=-R . This will give you that filter.

We have, in this description, sidestepped the issue of scaling. The gain of this
filter at resonance is

                                        2      3       4
    H(2πf) = G/sqrt[4(1-cos(2πfT))((1-R) -(1-R) )+(1-R) ].               Equ. 14

This looks a lot better if you substitute something, like D, for the term (1-R):

                                    2  3   4
    H(2πf) = G/sqrt[4(1-cos(2πfT))(D -D )+D ].                           Equ. 15

For most purposes, you  can normalize a filter  by multiplying it by  (1-R). For
higher frequencies, you  need a smaller factor.  The only problem is  that there
aren't  any more  multiplies left  in  a modifier  (no COEFF2,  just  COEFF0 and
COEFF1).  This means that you have to scale the signal going into the  filter by
(1-R) first. Don't forget this, because otherwise you will get lots of overflow.



                                    Page 55
LRNSAM                        Filtering algorithms                         DRAFT


Note also that COEFF0 and COEFF1 are really less than 1.0. This being  the case,
he said  inquisitively, how  can we possibly  synthesize 2*R*C  which can  be as
large as 2? You do that using the scaling specified in the mode word  MMODE. The
right four bits of  the mode are the scaling  for the multiplies. You  can scale
the result of the multiplication by COEFF0 and the multiplication by COEFF1 by a
power of two.  If we call the right  4 bits of the mode AABB, then AA or  BB set
to 0 specifies no scaling, 1 means scale by factor of 2, 2 by factor of 4, and 3
by 8.  AA is the scaling for the multiply with COEFF1, BB is the scaling for the
multiply with COEFF0. This means that we can get 2RC by setting COEFF0 to RC and
setting the scaling for the multiply by COEFF0 to 1 (factor of 2).

Now normalizing the  response at resonance  to 1 may or  may not be  exactly the
right thing to do. The problem is that this only makes sense if your signal is a
narrow band signal, like  a pure sinusoid or something.   Otherwise, normalizing
by the gain at resonance  is too much attenuation.  Unnormalized gives  too much
gain  and normalizing  by the  gain  at resonance  is too  much  attenuation, so
anywhere in between (like multiplying by (1-R)) is probably  reasonable. Figures
9 and 10 show filter responses for two-pole resonators at three different center
frequencies and a number of  different bandwidths.  Figure 9 shows  a normalized
filter. Notice that the gain is now 0 dB (1.0) at the center frequency,  but for
high values of R, the peak  is really narrow.  Figure 10 shows  the unnormalized
filter responses. Note that  at high values of  R, the filter can  emphasize the
signal by as much as 40 dB.

Normalizing things  with zeros rather  than poles is  much easier.   The maximum
response for a filter with zeros occurs either at zero frequency or at  half the
sampling rate, and this  maximum is guaranteed to be  less than 4.0 (for  a real
notch filter, rather than two first-order filters multiplied  together). Thusly,
you  can  pretty much  ignore  the scaling  of  an all-zero  filter  because you
probably won't get too screwed by it. Figure 11 shows the responses  of two-zero
filters (or antiresonators, as we might call them).

And  not to  leave out  the  first order  filters, figures  12 and  13  show the
responses of one pole and one zero filters. Here the maximum and  minimum values
occur only  at zero  and at  half the sampling  rate.  In  all these  plots, the
horizontal axis represents  frequency from zero to  half the sampling  rate. You
can see  that positive  values of  R for  the one  pole case  represent low-pass
filters. The frequency  responses of all-pole filters  are just the  inverses of
those for all-zero filters. Isn't that handy?










                                    Page 56
LRNSAM                        Filtering algorithms                         DRAFT


FPIX.XGP[DOC,MUS] goes here.  (FIGURE 9)
















































                                    Page 57
LRNSAM                        Filtering algorithms                         DRAFT


(FIGURE 10)
















































                                    Page 58
LRNSAM                        Filtering algorithms                         DRAFT


(FIGURE 11)
















































                                    Page 59
LRNSAM                                                                     DRAFT


(FIGURE 12, FIGURE 13)
















































                                    Page 60
LRNSAM                                                                     DRAFT


                                   Section 20
                                   _______ __

                                  Delay Units
                                  _____ _____



Delay units are called from modifiers by invoking the modifier in DELAY mode (in
a similar sense as invoking a modifier in, say, AMPLITUDE_MODULATION mode).  For
an example of calling a delay line see page 93.

The delay units  thus invoked have two  possible modes, table lookup,  and delay
line.  There is (yet another)  storage area that the delay units  address called
delay memory.  It  contains (in our  case) 48k of 20  bit words.  (A  full house
would contain up to 65,536 20 bit words.)

In delay line mode,  the modifier sends the delay  unit a word of data  which is
then stored in the current location  in the delay array.  The old value  in that
location is retrieved before the new word is written in.  This old word  is then
sent back  to the modifier  as the delayed  signal.  When the  array is  full of
words read from the modifier, the array pointer is reset to the base address and
it goes down the line again, retreiving the data in the location  before writing
new data received from the modifier into it.

Id     Definition            Function
------------------------------------------------------------------------------
P      MODE                  Delay unit mode
Z      DELAY_LENGTH          Delay line unit length
       SCALE                  or table address scale factor
Y      INDEX                 Running index of the delay array
X      BASE_ADDRESS          Base address of delay array

Modes:
       D_INACTIVE            dead
       DELAYLINE             does the processing described below
       TABLE_LOOKUP          see below
       ROUND_TABLE_LOOKUP    see below

                                    Fig. 14
                                    ____ __




20-1. Delay line mode

DELAYLINE

This mode can be SAILishly represented this way:


                                    Page 61
LRNSAM                            Delay Units                              DRAFT


for INDEX←BASE_ADDRESS step 1 until BASE_ADDRESS + DELAY_LENGTH do
    begin
        Result←TERM_0 + COEFF0*TERM_1;
        TERM_0←<delay memory data received>[INDEX];
        TERM_1←A_IN + COEFF1*<delay memory data received>[INDEX];
        <delay memory data sent>[INDEX]←TERM_1;
    end;


Please  note, that  the  delay arrays  are  not cleared  when  initiallized, and
therefore will return junk on their first pass.  Also, it requires an additional
three passes to return data from the delay unit, so the real turn around time is
DELAY_LENGTH + 3.



20-2. Table lookup mode

TABLE_LOOKUP

The data word received from the modifier is shifted to the right by DELAY_LENGTH
bits and then used  to address the memory area  assigned to the unit.   In table
lookup mode, the length  of blocks of memory are  always powers of two.   So the
address of any element in that block can be specified by a word whose  length is
that power (e.g., a block of 64  words takes a 6 bit address).  But the  size of
the word containing  the address which  is passed by  the modifier to  the delay
unit is 20 bits, and the address is left adjusted in it (in case you  needed the
full 20 bit address).  So the delay unit generates the correct address  by right
shifting until the address is right adjusted in the word, whereupon it becomes a
valid address.  The number of shifts necessary is stored in DELAY_LENGTH in this
mode (whereas in DELAY_LINE mode it really means the delay length).  The word in
the addressed memory location is returned to the modifier three passes later.



20-3. Table lookup - rounded

ROUND_TABLE_LOOKUP

Data received is shifted right, rounded then used to address delay memory.  This
is useful when  employing a block  of delay memory  to store things  like sound-
pressure functions or any table with critical values as it gives you  3dB better
resolution than straight  table lookup.  The difference  is like that  between a
digital oscillator  which looks  up its  table by  truncating the  address after
calculating the increment (which is essentially what TABLE_LOOKUP does)  and one




                                    Page 62
LRNSAM                            Delay Units                              DRAFT


which  rounds  first.   The  latter has  3dB  better  resolution.   (1)  The one
potential bug is if  the address is rounded up  off the last word of  the table.
The two options in designing the  machine were to wrap around like  MUSCMP does,
or to allow the  overflow.  The latter was  implimented for the reason  that, if
you were doing a square  root table, for instance, wraping around  wouldn't give
you any kind of a right  answer.  So you must sanitize any overflow  by sticking
some appropriate value in your table  in at the n+1th. location if you  use this
mode.





































_______________________
() The next more accurate way  to go is to interpolate between the  two adjacent
values in the table, which is what the ZOSCIL oscillator does in MUSCMP.

                                    Page 63
LRNSAM                                                                     DRAFT


                                   Section 21
                                   _______ __

                                Calling the box
                                _______ ___ ___



The lowest level of access (short of writing your own assembler) is to set  up a
SAIL program  which requires  the source file  LOWER.DEF[SAM,MUS], and  the load
module  LOWER.REL[SAM,MUS].  LOWER.REL  contains the  SAIL  procedures described
below which  handle the  set up  of data  paths in  the box  and the  passing of
parameters.  LOWER.DEF contains the macro  definitions of all the terms  such as
ANGLE and TERM_1 that are in the  tables of this document, plus a slew  of other
macros for  various obscure  system hacks.  This  is also  where the  arrays are
declared for  the unit generators  and sum memory,  and where the  procedures in
LOWER.REL are formally called.

The procedures are:



21-1. GET

External simple integer procedure GET(integer id).

This procedure  does a lookup  on the processing  element lists and  returns the
number  of  the lowest  numbered  free element.   For  instance if  tweet  is an
integer, saying:  tweet←GET(id_generator) will  return the  number of  the first
free generator in  tweet. Tweet then  has a value  which consists of  flags that
specify a particular  unit generator.  Likewise:  tweet←GET(id_modifier) returns
the  number of  the  first free  modifier, and  tweet←GET(id_delay)  returns the
number of  the first free  delay.  The  terms in the  definition list  below are
loaded into  your program by  LOWER.DEF and contain  addresses in  the assembler
such that  when you  call the  assembler with one  of these  variables in  a GET
command, the assembler is  pointed to that part  of itself which deals  with the
clerical work of claiming that type of process element.

Definition                 Function
--------------------------------------------------------------------------------
ID_GENERATOR               gets a generator.
ID_MODIFIER                gets a modifier.
ID_DELAY                   gets a delay unit.
ID_SUM_MEMORY              gets a sum memory location.

                                    Fig. 15
                                    ____ __


This works fine  for claiming processing elements,  but falls short  of adequate


                                    Page 64
LRNSAM                          Calling the box                            DRAFT


for sum memory, since one typically wants to select one particular coordinate or
other.   GET(ID_SUM_MEMORY)  only returns  the  absolute address  of  the lowest
numbered free sum memory location  regardless of coordinate.  To get  a specific
coordinate, one  must add  the sum  memory id  in with  one of  the id's  on the
following table.  For example GET(ID_SUM_MEMORY+THIS_MOD_PASS) returns the first
available this pass sum memory location.

Definition                 Function
--------------------------------------------------------------------------------
THIS_MOD_PASS              gets modifier this pass sum memory.
LAST_MOD_PASS              gets modifier last pass sum memory.
LAST_GEN_PASS              gets generator last pass sum memory.
THIS_GEN_PASS              gets generator this pass sum memory.

                                    Fig. 16
                                    ____ __




21-2. GIVE

External simple procedure GIVE(integer id).

This does  the opposite of  GET.  That is,  it frees a  unit generator  from its
associated variable. It also makes  that unit generator unknown to  the universe
until another GET is perpetrated. (However, you can't be assured of  getting the
same process element back, since  it always takes the lowest  numbered available
element.) A  sample command: GIVE(tweet).   It is not  necessary to  always GIVE
back processing elements.   All you are doing  is clearing an identifier  of its
association.  However, if your program is a dynamic allocation routine where you
may  be repatching  the box  on the  fly, if  you continued  glomming processing
elements  without releasing  any you  would soon  have more  processing elements
bound than you have processing ticks to run them with.  You would soon  drop out
of real time (how degrading!).




21-3. BIND

External simple integer procedure BIND(integer id, name, value).

This  allows  you to  associate  different elements  in  the box.  It  has three
functions: a) to associate input  data with fields in processing  elements, b)to
associate sum memory with input  and output ports of processing elements,  3) to
set the running status of processing elements. For instance:



                                    Page 65
LRNSAM                          Calling the box                            DRAFT


                            28
BIND(tweet, FREQUENCY, 440*2  /clock_rate)

sets the generator referenced  by the bits in tweet  to have a frequency  of 440
                       28
(That is, it puts 440*2  /clock_rate in FREQUENCY (GJ)).  This is an  example of
using  BIND to  associate data  with  a processing  element.  BIND  is  also the
procedure to associate  the output and  input addresses of  processing elements.
For instance, the following sequence will link two generators:

BIND(gen_sum,SUM_MEMORY,tweedle)
BIND(didi,FM,gen_sum)

This will give the address which is in location SUM_MEMORY in tweedle  (which is
the  address of  the sum  memory location  where tweedle  writes its  output) to
location FM in  didi.  When didi  reads that address  it will get  the deposited
output of  tweedle.  An example  of setting the  running status of  a processing
element might be: BIND(fut,MODE,A_RUNNING).  Valid constructs for integer <name>
are all the generator, modifier and delay line SAIL definitons  discussed above,
such as FREQUENCY, RATE, EXPONENT, MODE, A_IN, COEFF1, etc.



21-4. SET_OUTPUT

External simple procedure SET_OUTPUT(integer sail_channel).

SET_OUTPUT lets the user pass the  assembler command output not only to  the box
but to any number of  devices.  You must use this to  set up a path to  the box,
just as you must open any device before doing I/O to it.  But, you can also, for
instance, open the disk  and enter a file, and  pass the disk channel  number to
the assembler  with a SET_OUTPUT  and the assembler  will also pass  the command
stream to the opened file, thereby giving you a record of it.



21-5. SET_CHANNEL

External simple procedure SET_CHANNEL(integer data_chan,sail_chan).

There are "data"  channels between the  main computer and the  box as well  as a
"command" channel.  SET_CHANNEL allows the user to set a channel for  data input
                                                                      ____
and output to the box. This is  how the disk and the ADC (if  implemented) would
get data into the box.  Also, this  is how data would be forwarded from  the box
to the main computer.   This is not used for  the DACs, which have  another hook
into the box (they would be used  in conjunction with a generator set up  with a
BIND command to run mode WRITE_DAC,  see page 24).  The main use of  reading and


                                    Page 66
LRNSAM                          Calling the box                            DRAFT


writing samples to and from the operating system would be to do  manipulation of
sound files.  However, this means you must wait for disk ops, and since the disk
is the current  bottleneck on the  system, it probably  means that this  will be
basically a non-real time mode.  The following are the available  device channel
definitions:

Definition    Function
--------------------------------------------------------------------------------
DISK_OUT      output to the disk.
DISK_IN       input from the disk.
ADC_IN        input from the adc. (not likely soon).
*_IN          add your favorite device here.

                                    Fig. 17
                                    ____ __




21-6. SET_PROCEDURE

External simple procedure SET_PROCEDURE(reference procedure <name>).

SET_PROCEDURE  allows  the user  to  choose  a home  grown  procedure  for error
recovery.  LOWER.REL contains  a slew of error  traps.  The error  handler first
stops execution then, assuming you have declared an error procedure of  your own
with SET_PROCEDURE, it passes control  to your procedure and passes a  SAIL type
string to it containing a thumbnail characterization of your problem.   What you
probably want your  error procedure to  do is to save  the current state  of the
world, close files that were open and generally make a controlled  retreat.  You
DON'T want it  to try to  continue execution, since things  aren't set up  to do
that.  At this stage of things, all errors are fatal.  An instance:

simple procedure BOX_ERROR(reference string errmsg);
  begin
    print('11&"You have done the following no-no:"
        "&errmsg&'15&'12);
    close(input_file);
    close(output_file);
    release(input_file);
    release(output_file);
    usererr(0,0,"Fatal low level error...");
  end;
SET_PROCEDURE(box_error);






                                    Page 67
LRNSAM                          Calling the box                            DRAFT


21-7. DECODE


external simple integer procedure DECODE(integer unit).

When passed an id that has been GETed, DECODE returns the type of unit, numbered
as follows:

ID_GENERATOR   0
ID_MODIFIER    1
ID_DELAY       2
ID_SUM_MEMORY  3



21-8. RELATIVE


External simple integer procedure RELATIVE(integer unit).

When passed an id that has been GETed, RELATIVE returns the number of  that unit
relative to the base address of that type unit.



21-9. SET_MODE

External simple procedure SET_MODE(integer name).

This procedure is used to set various states of the whole box, such as number of
ticks per pass, etc.

Code   Definition                Function
--------------------------------------------------------------------------------
CONO-A RESET_TICK_COUNTER        to beginning of pass (if stopped).
CONO-A PERMIT_PROCESSING_TICKS
CONO-A INHIBIT_PROCESSING_TICKS  only update ticks are executed.
CONO-A RESET                     master reset.
MISC   WAIT_CLEAR                start up all waiting process elements.
MISC   PAUSE_CLEAR               start up all pausing process elements.
MISC   STOP                      (screeech...)
       OPTIMIZE                  pack as many commands per word as possible
                                 (possibly dangerous: see below).
       NON_OPTIMIZE              pack one command per word,
                                 but maybe don't run so efficiently.

                                    Fig. 18
                                    ____ __


                                    Page 68
LRNSAM                          Calling the box                            DRAFT


Example: SET_MODE(PAUSE_CLEAR).

OPTIMIZE mode is potentially dangerous because in optimizing, some  commands may
get  rearranged  in time  in  a possibly  fatal  way.  Mark  Kahrs  supplies the
following example.  Suppose you want to change several commands, like:

        bind(i1,mode,5);
        bind(i1,fm,37);
        .
        .
        .
        bind(i1,sum_memory,sum_outa);

Providing there are no dwells...  (Because a dwell causes the packing  tables to
be cleared), then the sum_memory change will occur RIGHT after the  mode change,
i.e., slightly out of order.  This is only a problem if the commands in question
span more than one pass, but  the chances of that happening are not  too likely,
even then there is a question  whether it would be noticable, so this  is mostly
theoretical nitpicking.  In general OPTIMIZE mode should work just fine.



21-10. SET_FIELD

External simple procedure SET_FIELD(integer name,field).

Use this to set the quantities of the various things described below:






















                                    Page 69
LRNSAM                          Calling the box                            DRAFT


Id     Definition            Function
--------------------------------------------------------------------------------
CONO-A CONTROL_MODE          called only for three definitions below.
             C_START         start the box.
             C_TICK          cause one tick.
             C_STOP          stop the box. This command is identical to
                             STOP above, except that this one is
                             used by the controlling
                             system for interupts.
TICKS  PROCESSING_TICKS       set how many processing
                             ticks per pass.
TICKS  TOTAL_TICKS           set how many of both kinds of
                             ticks per  pass.
                             update ticks/pass = (total ticks
                             - processing ticks) / pass.
TIMER  DWELL                 process no more update ticks until
                             pass counter = data.
TIMER  SET_PASSES            set pass counter to data.
TIMER  CLEAR_PASSES_DWELL    clear pass counter then
                             DWELL until pass
                             number equals argument.
       SIZE_BUFFER           controls size of command buffer between
                             the controlling program and the box.
                             Initialized to 1024.
       SIZE_COMMANDS         Sets how many commands will be sent
                             to the buffer at once.
                             Initialized to 64.
       PACKING_MODE          called only for three definitions below:
             RIGHT_JUSTIFIED take low order 20 bits of source, pack
                             into low order end of box destination field.
             LEFT_JUSTIFIED  take low order 20 bits of source, pack
                             into high order end of box destination.
             FULL_WORD       low order 28 bits packed into destination word.

                                    Fig. 19
                                    ____ __

Example:
SET_FIELD(PROCESSING_TICKS, 300);
SET_FIELD(DWELL, CLOCK_RATE*5);
SET_FIELD(PACKING_MODE, FULL_WORD);
SET_FIELD(CONTROL_MODE,C_START);



21-11. BIND_FIELD

External simple integer procedure BIND_FIELD(integer id, name, value, type).


                                    Page 70
LRNSAM                          Calling the box                            DRAFT


This acts just like BIND above, but it has an additional field which  allows you
to temporarily (for this call only) change the packing mode.  For example,

BIND_FIELD(foo,frequency,440*mag,left_justified)

will select that  packing mode for passing  frequency this time, then  revert to
whatever it was before.



21-12. Size of command buffer

There is a problem  posed by the size of  the buffering and size of  the command
block.  The command buffer is only flushed (passed to the box) when it  is full.
So, if the size of the buffer is much bigger than the buffer inside the box, you
run the risk of command underrun  since the box's internal buffer could  run out
of commands before the next block is put out.  If the buffer is small,  there is
less likelihood of  underrun up to  a point.  The catch  here would be  that you
will need more I/O operations from the main computer system, and it may  (in its
infinite wisdom) decide not to give  them to you when you need them.   And since
you have  only a  small buffer  full of commands,  you may  run out  soon, again
resulting in underrun.   Richard Almaniac would  probably have something  to say
about this. (Don't count your ticks until they're passed?)



21-13. LOAD_DELAY

External simple procedure LOAD_DELAY(from_address,to_address,count).

This stuffs arrays into delay memory for table lookups, etc.  The data format is
that which is  set by SET_FIELD, see  above. Notice that delay  memory locations
are not arbitrarily associated with  specific delay units, hence any  delay unit
is free to read any (contiguous) part of delay memory.



21-14. INITIALIZE

External simple procedure INITIALIZE.

Initializes the code (useful in case of a restart).







                                    Page 71
LRNSAM                          Calling the box                            DRAFT


21-15. FLUSH

External simple procedure FLUSH.

FLUSH should be called before exiting the user program.  It forces data  left in
all buffers to be written out.  Failure to do so results in missing words at the
end.  So please, FLUSH when you're through... and wash your hands.



21-16. Processing element arrays

This next bit is a list  of the arrays that are declared  in LOWER.DEF[SAM,MUS].
They are modified SAIL type arrays.  The purpose of these arrays is to  keep tab
of  the processing  elements and  memory locations  in use.   When  a processing
element is in any mode other than inactive there will be a non-zero value in its
corresponding array location.  Notice that there is one big array from 0:671 and
several smaller ones  that add up  to a total  of 672.  Actually,  they overlap,
such that  the location  referenced by  unit_generators[0] is  the same  as that
addressed by  generators[0], and  location unit_generators[256]  is the  same as
modifiers[0], etc.  The point of including these arrays is so that you can write
your own procedures to do the  book keeping for the process elements  instead of
using  GET and  friends.   Also notice  that  all_sum_memory[0] is  the  same as
gen_sum_memory[0], and all_sum_memory[128] is mod_sum_memory[0].  The arrays are
allocated as follows:
























                                    Page 72
LRNSAM                          Calling the box                            DRAFT


--------------------------------------------------------------------------------
External integer array unit_generators[0:671].
   All the generators, modifiers
   delay units and sum memory addresses are listed here
   as to availability.

External integer array generators[0:255]. Just the generators.

External integer array modifiers[0:127]. Just the modifiers.

External integer array gen_sum_memory[0:127].
   Just the generator sum memory.

External integer array mod_sum_memory[0:127].
   Just the modifier sum memory.

External integer array gen_this_pass_sum_memory[0:63];
external integer array gen_last_pass_sum_memory[0:63];
external integer array mod_this_pass_sum_memory[0:63];
external integer array mod_last_pass_sum_memory[0:63];
   Individual quadrants.

External integer array all_sum_memory[0:255]. Both sum memories.

External integer array delays[0:31]. Just delay units.

                                    Fig. 20
                                    ____ __




21-17. Tick counters

The next two variables  keep track of the  current update tick, and  the current
pass number.

external integer update_tick.

external integer pass.



21-18. Steps in calling the box

There are some important  things to realize about  the order in which  modes and
fields are set in the box.  Most obviously, you must set or clear all  fields in
the box before you crank it up.  Any processing chains must be started up in the


                                    Page 73
LRNSAM                          Calling the box                            DRAFT


order that they reference each other.  Also, if modifiers are reading  from this
pass sum memory, the writing processing element must have done its  thing before
the reading modifier  (meaning the writing element  must be lower  numbered than
the reader).  So a typical program will do the following in roughly this order:
    -Require LOWER.DEF source_file and LOWER.REL load_module,
    -Do a SET_OUTPUT to pass a command channel to the assembler,
    -GET the desired processing elements,
    -Including generators to output samples to the DAC, if desired
    -Calculate the number of ticks needed, pass that with SET_FIELD
        to the assembler,
    -Set the corresponding sample rate, and magic numbers,
    -Set all unused processing element fields to reasonable values
        with BIND,
    -Set any unused FM ports in generators or A_IN type ports in
        modifiers to some unused (hence 0) sum memory location
        with GET and BIND,
    -Set all used fields to their desired values with BIND,
    -Connect processing elements to sum memory
        (and hence to each other),
    -Start the processing elements in order of reference
        (i.e., start the generator passing samples
        to the DAC last),
    -DWELL until the event requires more update ticks,
    -or if it's finished, GIVE back the used process elements
        and sum memory locations when no longer needed so other
        processors can get high-ordered time slots, keeping you in
        real time.
    -lastly, FLUSH!





















                                    Page 74
LRNSAM                                                                     DRAFT


                                   Appendix 1
                                   ________ _

                         An introduction to pipelining
                         __ ____________ __ __________



There are a number of ways to accomplish digital sound synthesis.  If we  used a
general purpose computer to do the processing for us, depending on which kind we
used and how busy it was otherwise, we might be able to accomplish our goal, but
there would be lots of wasted motion.  General purpose computers are meant to be
able to handle a wide variety of kinds of jobs.  Consequently, there is  lots of
extra baggage  that they carry  along to allow  this flexibility.   We, however,
have a rather specific job to do: most signal processing jobs involve applying a
long series  of data to  a relatively fixed  complex calculation.  So  the first
thing to do if we were designing a computer for our purposes is to dump  as much
of the overhead and waste motion  as possible (however, it would be nice  if the
device we were to build were  controlled by a large general purpose  computer so
that we could have as flexible a way as possible to run it).

Let us take the following equation as an example of a calculation we  might want
to do with our machine.  It  produces a cosine wave when fed lots  of increasing
values of I,


   Y ← A*SIN(6.28138*(I/512) + π/2).                                     Equ. 16

Our calculator will only need to be  able to add, to multiply and divide  and to
look up values in a sine table.  It would do the calculations in stages, just as
we would by hand: it would  form intermediate results and then combine  them and
put out the result.  Such a machine, without the overhead of a general computer,
could be made to be very fast.

But this still has  some problems.  What if the  formula we want were  still too
time consuming to  calculate at the  rate of speed we  want?  What if  we wanted
lots of these formulas to be running simultaneously and added together?   How do
we expand this basic process but still keep the average computation time down?

If we just got lots of these  processors and fed each one a number we  would cut
the average compute time by  1/<the number of extra processors>.  But  we'd need
some fancy system of passing the numbers out and collecting the results.

A better approach is  to realize that, since  the calculation is broken  down by
the  processor  into  intermediate  steps, it  might  be  possible  to  have one
processor working on several values of  I at once.  To see how this  can happen,
we must  know the difference  between block structure  and pipelining.   We have
been  unconsciously  treating  the  processing of  the  formula  above  in block
structure.   In this  mode,  we take  a  number for  I,  subject it  to  all the


                                    Page 75
LRNSAM                   An introduction to pipelining                     DRAFT


intermediate computations and wait for a final value for Y to pop out  before we
take another value for I.  But what if we did it this way:

Lets make an "assembly line" out  of the process.  A belt carries in  the values
of I, lets say  we have 5 values.  The  workers along the belt are  the divider,
the multiplier, the adder and the sine-table-looker-upper, standing in the order
that we would normally solve the equation.  Here comes the first I.  The divider
picks up his number cruncher and  divides it by 512.  Meanwhile the rest  of the
guys multiply, add and sine-table-lookup  whatever junk the janitor left  on the
belt last  night, maybe his  lunch or something.   They all place  their results
down on the belt, and it is carried along to the next one.  The divider's number
is now in front of the guy who multiplies by 2π.  Meanwhile, THE NEXT VALUE OF I
comes down the chute  towards the divider!  Notice  we don't have a  valid value
for Y at the other end yet.   But if we continue, we will.  So, the  divider and
the 2π multiplier get to work doing their thing, meanwhile, the rest of the guys
continue playing with  the janitor's lunch  as it moves  past them on  the belt,
rolling it into little balls and throwing it at each other.  They set down their
results, and now the adder gets the intermediate value of what was the  first I,
the 2π multiplier gets the next, and the divider gets yet another new I.  On the
next shift, the  sine-table-looker-upper does his thing,  and the value  for the
first I is  through the process,  transformed into Y.   Behind it are  the other
intermediate values.  So we need to crank the whole process three more  times to
get the  5th Y  value through  the line.   But notice,  during these  next three
passes, we get a valid Y  value every time the assembly line turns  around once.
That means  we get  a good  result in the  time of  only one  intermediate step,
whereas in the block process, we  would have got only one result for  every five
intermediate steps.  This  is a nice increase  in average computing  time.  It's
called pipelining.

Lets look at some  of the implications of this  process.  First of all,  all the
inner operations are similar in that they constitute the picking up of a number,
the operation  on it,  and replacing  it.  We'll  call this  basic task  a pass.
Furthermore, it should  be obvious that the  system cannot work any  faster than
the slowest processor.  The minimum execution time of a pass then is the time it
takes  for  the  slowest  computing  element.   So  the  accomplishment  of  one
intermediate result consists of a pass.

Notice  that the  processor must  be primed  like a  pump., but  once it  is, it
produces one good value of output every pass.  In general, if the pipeline  is N
elements long, we  must add N passes  to the number of  passes it would  take to
send all the numbers through the pipeline.

Notice also we must throw away the first N samples we get of the output.  In the
assembly line above, the processor was  four units long, and we had  five values
of I to calculate, so it took  nine passes to accomplish the task, and  we threw
away the first four values of  the output since they were undefined  (unless you
knew the janitor).


                                    Page 76
LRNSAM                                                                     DRAFT


                                   Appendix 2
                                   ________ _

                           Time division multiplexing
                           ____ ________ ____________



The Samson box is extensively pipelined.  Just how extensively we will soon see.
Two of the essential ingredients in the box are called processing elements.  One
of them is  called a modifier,  the other is  called a generator.   The modifier
does various flavors of arithmetic and logic testing, while the generator does a
fancier version  of our example  formula, but with  lots of bells  and whistles.
Pete Samson claims there  are 256 generators and  128 modifiers in his  box.  In
fact, there's only one  nest of electronics for  the generators and one  for the
modifiers.  What really goes on is that there are 256 sets of parameters for the
generator  and 128  sets  of parameters  for  the modifier,  and  the processing
elements zap from one set to the next at a truely blinding rate of  speed.  This
is called time division multiplexing.

To get the full picture, reload  your mental image of the example  of pipelining
above.  Think of it maybe this way:

 I[1]   I[2]    I[3]    I[4]    I[5]    *       *       *       *
__↓__
| / |
| * |
| + |
|sin|
--↓--
  *     *       *       *       Y[1]    Y[2]    Y[3]    Y[4]    Y[5] 


                                    Fig. 21
                                    ____ __


Each step the  box takes from  left to right accomplishes  one pass, and  on the
fifth pass, we start geting valid Y values.  Now imagine the process if  we want
to deal with many  streams of data.  All we  need do is interleave  the separate
streams.  Input I[1] will come out as  output X[1], J will come out as Y,  and K
as Z.








                                    Page 77
LRNSAM                     Time division multiplexing                      DRAFT



 I[1] J[1] K[1] I[2] J[2] K[2] I[3] J[3] K[3] I[4] J[4] K[4]
__↓__
| / |
| * |
| + |
|sin| 
--↓--     
  *     *    *    *  X[1] Y[1] Z[1] X[2] Y[2] Z[2] X[3] Y[3] Z[3]...


                                    Fig. 22
                                    ____ __

Before analyzing this, let's redefine  our terms.  What we were calling  a pass,
                                                                           ____
lets now call a tick.  So a tick is the act of moving the processor,  sucking up
                ____
a number and spitting out another.  Now let's redefine "pass" to mean that point
where we have produced one valid  output for all of the streams.   (Don't worry,
we'll stick to this definition. The  term pass can be used in  simple situations
like our first example,  but from here on we  reserve the word pass to  refer to
the period culumnating in  the output of samples.) So,  where is the end  of the
first pass?  Right!, at Z[1], after  seven ticks.  Where is the end of  the next
pass?  Right again, at Z[2], after  three ticks.  From here on, a  pass consists
of three ticks only.   Since one pass equals one  valid output, if we  have five
each of I, J and K, it will take five passes of three ticks per pass to get them
all through  PLUS four  ticks to  prime the processor  for a  grand total  of 19
ticks.

We can deduce from this that the number of ticks per pass is equal to the number
of parallel data streams, also that the total number of ticks is equal to:


   <# of streams>*<length of longest stream> + <length of pipeline>.     Equ. 17

Remember that  a pass consists  of all the  processing necessary to  produce one
complete set of  output data, that  is, one sample.   The tick is,  therefore, a
subspecie of the pass.   The tick is the smallest  unit of time our  process can
use and the time it takes is the time of the slowest process.












                                    Page 78
LRNSAM                                                                     DRAFT


                                   Appendix 3
                                   ________ _

                              Basic number theory
                              _____ ______ ______



Trying to make representations between different number systems is  somewhat the
same problem as finding suitable translations for unidiomatic foreign  terms, we
have to agree on a meaning.

Modern computers represent  numbers in a binary  system.  Briefly what  a binary
number consists of in  such a machine is a  group of electronic devices  each of
which can  be "on" or  "off", depending on  the number being  represented.  Each
device stands for a particular  weight or magnitude, and the  number represented
by the set of devices is the  weighted sum of the ones that are "on".   Thus, if
we let  three printing  columns represent three  such devices  and we  wanted to
represent the number 3 we would do this;:

weight:          4       2       1
state:           0       1       1

                                    Fig. 23
                                    ____ __


where a 1 means "on" and 0 means "off".  The word binary refers to the fact that
the  devices have  only these  two states.   The general  expression for  such a
number is


   m      2    1    0       -1     -2        -n
b 2 +...b  +b 2 +b 2  ∧ b  2  +b  2  +...b  2
 m       2   1    0      -1     -2        -n


where the "∧" is the "binary point" (think of decimal point).  The b term is the
state, m is the largest weight, n the smallest.  The largest unsigned integer we
                          m
could represent would be 2 -1 where m is the number of places, or "bits" left of
the binary  point.  In  Fig. 23,  7 is  the largest  number we  could represent,
starting from zero.

For representing  the domain of  positive and negative  numbers there  are three
basic systems used: sign-magnitude,  one's complement and two's  compliment.  In
the  sign-magnitude system  the leftmost  bit, called  the most  significant bit
(MSB), or high order bit, is used  to denote the sign of the number,  zero being


                                    Page 79
LRNSAM                        Basic number theory                          DRAFT


taken as positive, one as  negative.  So the highest integer magnitude  that can
                   m-1
be represented is 2   -1.

One's complement and two's complement do things a little differently.   Here the
negative  numbers are  expressed  as the  logical complement  of  their positive
counterpart.  Complementation is done by changing all the zeros to ones and vice
                                                                             n
versa.  For  one's complement the  formula for the  negative equivalent  is 2 -x
where n is the number  of bits and x is  the number to be complemented.   So the
one's complement  of 001  is 110,  which we represent  in decimal  as +1  and -1
                                                       n            n
respectively.  For two's  complement the formula  is (2 -1)-x or  (2 -x)-1.  For
example, to form the two's complement of 010, subtract one = 001, and complement
=  110.   Or, equivalently,  complement  010 =  101  and add  one  to  the least
                                                         ___
significant bit (or LSB) = 110.  To show this graphicly:
binary two's   one's   sign-magnitude
011    +3      +3      +3
010    +2      +2      +2
001    +1      +1      +1
000    +0      +0      +0
111    -1      -0      -3
110    -2      -1      -2
101    -3      -2      -1
100    -4      -3      -0

Notice that the one's complement and sign-magnitude have two representations for
zero, whereas the  two's complement has  one zero and  it is taken  as positive.
Two's complement also has one negative number greater than its  largest positive
number.   This  number has  no  complement.   Notice that  the  two's  and one's
complement  system  "wrap  around"  from the  greatest  positive  number  to the
greatest  negative  number  when the  greatest  positive  number  is incremented
whereas the sign-magnitude system goes to zero again.

Now the Samson box uses two systems, the two's complement for signed numbers and
plain binary for unsigned  numbers.  Two's complement offers some  advantages to
the computer designer and is nearly universal (exept for certain CDC machines!).

So far so good, now what if we wanted to represent fractional numbers instead of
integers?  Well, for example, using  the unsigned system, and expanding  to four
bits it might look something like this:








                                    Page 80
LRNSAM                        Basic number theory                          DRAFT


binary   fractional    decimal
1111     nearly 1.0    15
1110                   14
1101                   13
1100     .75           12
1011     nearly .75    11
1010                   10
1001                   9
1000     .5            8
0111     nearly .5     7
0110                   6
0101                   5
0100     .25           4
0011     nearly .25    3
0010                   2
0001                   1
0000     .0            0

                                                  n
The fractional value is then the <decimal value>/2 .  The numbers just below the
the ones that have only one bit  on (.5, .25, etc.) are often taken as  being an
equivilent for  the ones with  only one bit,  which is why  I say they  are, for
example, nearly .5, etc.

The two's complement version of this is:
binry    fractioal     decimal
0111     nearly 1.0    7
0110                   6
0101                   5
0100     .5            4
0011     nearly .5     3
0010     .25           2
0001     .125          1
0000     .0            0
1111     nearly .0     -1
1110     -.125         -2
1101     -.25          -3
1100     -.5           -4
1011                   -5
1010                   -6
1001     nearly -1.0   -7
1000     -1.0          -8

Here we have a  -1.0, but only a +.999...  or nearly +1.0.  So it  conforms with
the two's complement system which has one greater negative value than positive.

This has the formal name of two's complement fixed binary point notation.  It is


                                    Page 81
LRNSAM                        Basic number theory                          DRAFT


used everywhere  in the box  that a  signed system is  needed, most  notably for
waveforms, which are representations of positive and negative fluxuations around
normal air pressure taken as zero.   However, it is not used in  simple counting
situations such  as ANGLE  or EXPONENT  where the  unsigned integer  notation is
used.

Notice the sequence in the fractional notations that .5 = 0100..., .25 = 0010...
and .0125 = 0001... (where "..."  stands for any additional bits to  the right).
From this it can be seen that to multiply a number by a (positive) power of two,
N, requires only that it be shifted  left N places.  For division by a  power of
                                                                              2
two, shift  right.  This also  holds for integer  binary systems: ...0010  = 2 ,
           4
...0100 = 2 , etc.

Be careful as to whether a  number is signed or unsigned when combining  them in
expressions as  signed numbers are  right shifted one  bit compared  to unsigned
numbers (which use the high order bit for magnitude, not sign).

This also brings  up the general problem  of adding words of  different lengths.
Clearly, to get a  valid result for an  addition or subtraction, the  weights of
the terms must match.  If the  words are of different lengths, the  procedure is
to first normalize the two numbers  (line up the weights), then extend  the sign
bit of the shorter word out to the limit of the most significant bit, then add.

























                                    Page 82
LRNSAM                                                                     DRAFT


                                   Appendix 4
                                   ________ _

                                 SAIL examples
                                 ____ ________





4-1. Generator example

What we want to do now is to set up some intermediate level routines out  of the
low level that will do such basic things as set up DAC channels,  sampling rate,
magic numbers, file output, etc.  Then we'll call the procedures in  the context
of a couple of abbreviated  programs.  These procedures will be specific  to our
example  programs;  to make  them  truly general  might  obscure  their didactic
purpose.

Our first "piece" will be  to generate 6 seconds of  a sine wave at A  440.  For
you analog  synthesizer cognoscenti, I've  tried to write  this example  to make
sense in terms of the equivalent patching procedure.

The first  procedure will  set up  a generator and  a sum  memory location  as a
channel to a DAC.  Assume from now on that ∂ stands for COMMENT.

integer procedure GET_OUTN(integer chan; reference integer gen_out);
        ∂ Gets DAC output generators;
    begin
        integer sm;
        gen_out←get(id_generator);      ∂ gen_out now has addr. of 1st. 
                                          free generator;
        sm←get(id_sum_memory);          ∂ sm gets addr. of 1st. free sum memory;
        bind(gen_out,fm,sm);            ∂ gen_out's fm input now reads contents
                                          of sm;
        bind(gen_out,sweep,chan);       ∂ DAC addr. is put into the sweep slot;
        bind(gen_out,mode,dac_write);   ∂ Start up generator feeding DAC;
        return(sm);                     ∂ this addr. will be used by generators 
                                          desiring output to DAC;
    end;


This procedure  sets the  clock rate from  the number  of processing  and update
ticks.  Here  srate is  the sampling rate,  frmag the  magic number  for scaling
frequencies, dfrmag is for  SWEEP and gpmag is  for RATE.  These are  all global
variables which are set here.

procedure SET_CLOCK(integer nptix,nutix); 
    begin
        set_field(processing_ticks,nptix);

                                    Page 83
LRNSAM                           SAIL examples                             DRAFT


        set_field(total_ticks,nptix+nutix);
        srate←1/(0.000000195*(nptix+nutix+9));
        frmag←(1 lsh 28)/srate; ∂ frmag is the scaling 
                for FREQUENCY;
        dfrmag←frmag/srate; ∂ dfrmag is the scaling for SWEEP;
        gpmag←(1 lsh 24)/srate; ∂ gpmag is the scaling for RATE;
    end;


This next  procedure will set  up a file  to record the  command stream.   To do
this, get a system channel, open a disk file on it, then pass the channel number
to SET_OUTPUT, which will take  any code and pass a  copy of it to the  file, as
well as  to the  box.  Getchan and  open are  predeclared SAIL  procedures which
return an available system channel  and open it, respectively.  Outstr  prints a
message on  a teletype,  inchwl inputs characters  to a  string from  a terminal
until it gets a line feed.

procedure FILOUT;
    begin
        string s;
        com_chan←getchan;
        open(com_chan,"dsk",'17,0,0,0,eof,fail);
        do begin
                outstr("output file = ");
                enter(com_chan,s←inchwl,fail);
        end until not fail;
        set_output(com_chan); ∂ this passes the channel number 
                to the box assembler;
    end;


The next two routines will setup and cleanup the box for this example.   Zero is
a global integer which will contain the addr. of a sum memory location  which we
will never write into, and which  will hence always return zeros. (1) Outa  is a
global integer which will have a DAC addr.  set in GET_OUTN above.   Gen_outa is
another global  integer which will  be the addr.  of the generator  in WRITE_DAC
mode.  The  BIND command makes  the output of  the unit generator  producing the
sound go to outa, which is read by gen_outa and passed to the DAC.

procedure INIT;
    begin
        INITIALIZE;             ∂ lower level initialization;
        zero←GET(id_sum_memory);
        SET_CLOCK(64,64);       ∂ will produce a 38.4kHz sampling rate;
        outa←GET_OUTN(1,gen_outa); ∂ a generator now desiring to pass 
_______________________
(1) Since processing elements read sum  memory whether we like it or not,  if we
don't want junk coming into it we must set it's input to read from zero.

                                    Page 84
LRNSAM                           SAIL examples                             DRAFT


                samples to DAC 1 need only put the samples in sum memory in
                location OUTA;
        FILOUT;
    end;


This next  procedure will  clear the box  for this  example.  Giving  back these
elements is not really necessary at this point since our "piece" ends  here, but
in principle  whenever you aren't  using a processing  element, GIVE it  back so
subsequent GETs don't continually gobble up processing elements.

procedure FINISH;
    begin
        GIVE(gen_outa);
        GIVE(outa);
        FLUSH; ∂ don't forget to wash your hands;
        close(com_chan);
        release(com_chan);
    end;


These  next two  procedures stuff  values  into a  generator.  We  only  want to
control the frequency and amplitude without anything else fancy in this example,
but  we must  zero the  things  we don't  want.  We'll  break it  down  into two
procedures, one for frequency information, another for amplitude and enveloping.

procedure SET_GEN(integer ug; real freq);
    begin

        BIND(ug,sweep,0);       ∂ set up oscilator params., this one for 
                                    sweep rate = 0;
        BIND(ug,angle,0);       ∂ initial angle = 0, so we get a sine wave.
                                    Had it been (2↑20)/4, it would
                                    have been a cosine wave, since
                                    it would start at 90 degrees;
        BIND(ug,fm,zero);       ∂ the FM term is always read, 
                                    so if not wanted point it into
                                    sum memory where it will only
                                    get zeros;
        BIND(ug,scale,0);       ∂ scale must be set to zero when not using
                                    sum of cosines mode;
        BIND(ug,frequency,frmag*freq);  ∂ set frequency;
        BIND(ug,sum_memory,outa);       ∂ pass it's output to outa;
    end;





                                    Page 85
LRNSAM                           SAIL examples                             DRAFT


This  next will  setup or  update  a generator's  envelope side,  and  start the
generator.  We'll use  an envelope shape that  has a positive going  attack from
zero  amplitude to  1. Gpmag*duration  gives an  increment size  that  will step
through  these values  in that  time.  The  steady state  will be  flat,  so the
increment will be 0.   The decay goes from 1  to zero, so the increment  will be
gpmag*-duration. Another way to do this would be to use the  C_RUNNING generator
mode.  Envstate will be tested for  envelope status: if envstate < 0  then setup
an attack, if = 0 then setup a steady state, else setup decay;

procedure SET_ENV_AND_DWELL(integer ug; real duration);
    begin
        own integer envstate;   
        if envstate < 0 then
            begin "ATTACK"
                BIND(ug,exponent,0);            ∂ set up initial point in ramp;
                BIND(ug,asymptote,0);           ∂ set up envelope d.c. offset;
 	              BIND(ug,rate,gpmag*duration);   ∂ set attack ramp increment;
                envstate←0;
            end "ATTACK" 
        else if envstate = 0 then
            begin "STEADYSTATE"
                BIND(ug,rate,gpmag*0);  ∂ ramp increment is zero during
                                                  steady state;
                envstate←1;
            end "STEADYSTATE"
        else begin "DECAY"
                BIND(ug,rate,gpmag*-duration);  ∂ ramp increment is negative
                                                  during decay;
                
                envstate←-1;
            end "DECAY";
        SET_FIELD(dwell,duration*srate);                ∂ set dwell time;
    end;


Now for the body of the program.  Assume the above procedures are put  in before
the first executable statement.

BEGIN "SIMP"
define ∂ = "COMMENT";
∂ get the source file definitions;
Require "LOWER.DEF[SAM,MUS]" source_file; 
Require "LOWER.REL[SAM,MUS]" load_module; 

integer  
simp,                   ∂ will contain the address of the sound producing 
                          generator;
zero,                   ∂ will be given a sum memory addr. not written into;

                                    Page 86
LRNSAM                           SAIL examples                             DRAFT


gen_outa,               ∂ will be the generator in WRITE_DAC mode;
outa,                   ∂ will contain a sum memory address connected to a 
                          generator in WRITE_DAC mode;
srate,                  ∂ will be the sampling rate;
frmag,                  ∂ magic number for FREQUENCY;
dfrmag,                 ∂ magic number for SWEEP;
gpmag,                  ∂ magic number for mumble;
com_chan,brk,eof,fail;  ∂ these are for SAIL i/o ;

∂ the above procedures go here;

∂ here begins the program initialization;
simp←GET(id_generator); ∂ gloms a generator and returns its address;

INIT;

SET_GEN(simp,440);                      ∂ setup arguements to simp;

SET_ENV_AND_DWELL(simp,2);              ∂ now we get the attack;

∂ here ends the initialization.  All parameters must be in place before
        any run modes are passed;
BIND(simp,mode,a_running+lplusq+sine);∂ beat me daddy, 8 to the bar...;


SET_ENV_AND_DWELL(simp,2);              ∂ now the steady state;
SET_ENV_AND_DWELL(simp,2);              ∂ now we get the decay;

GIVE(simp);
FINISH;
END "SIMP";



4-2. Modifier example

The next example will be a problem using modifiers to produce a scaled  range of
random numbers which can be  used to drive generators or other  modifiers.  Then
I'll show how to use a  generator and scaling modifier to produce  random ranges
of time durations.

First then,  say we wanted  a random  number scaled between  a certain  range to
control the frequency of a generator so as to get one random pitch per note.  To
do so we would, for instance, set up a modifier in TR_U_NOISE mode and apply its
output to the  generator.  We want  TR_U_NOISE if we  want a pitch  selection to
stay over many passes, i.e. the  length of the note.  The modifier  is triggered
by a non-zero value in some sum memory location that it reads.  Then we  turn it
off on the next pass before the modifier can trigger on it again.

                                    Page 87
LRNSAM                           SAIL examples                             DRAFT


Since the generator's only input inside  the box comes from its FM  port, that's
where we will read it in.  The first problem we face is that the number, call it
x, coming in from the modifier has a range of -1 to +1, but we want a number, Y,
that has a range between frequencies  a and b.  The general formula  for scaling
is

        x  - L0
  Y =   -------*(a - b) + b
        L1 - L0
where  L1 is  the upper  bound and  L0 is  the lower  (in this  case 1  and -1).
Substituting for the L terms this becomes

        x+1
    =   ---(a-b) + b.
         2
But we don't have a modifier that  can do all of this at once. Recall,  that the
algorithm for TR_U_NOISE is:


Result ← COEFF0*TERM_1 + TERM_0.  If B_IN*COEFF1 ≠ 0 then TERM_1←result.


So we have to do a little juggling:

        x+1
    =   ---(a-b) + b
         2

         a-b   a-b
    =  x*--- + --- + b
          2     2

         a-b   a+b
    =  x*--- + ---
          2     2

We can precalculate (a-b)/2 and give it to COEFF0 of the modifier where it would
be multiplied by  x stored in  TERM1.  That takes care  of the first  term. That
leaves us with the second term  (a+b)/2.  This can be inserted in  the FREQUENCY
port  of the  scaled generator  on an  update tick.   When the  FM port  of this
generator picks up the result of the partial scaling from the modifier,  it will
add the contents of FREQUENCY to it, and we get the whole expression.

Some technical problems remain.  We must expand our earlier  procedures somewhat
and invent some new ones.  First, SET_GEN  now needs to be able to set up  an FM
read  address from  sum memory.   We need  a procedure  called SET_MOD  to  do a
comparable thing  for modifiers.   Furthermore we now  need to  create a  way to


                                    Page 88
LRNSAM                           SAIL examples                             DRAFT


trigger the TR_U_NOISE modifier.  Of the uncountable ways to stuff  numbers into
sum memory, we'll crank up  another generator in PULSE_TRAIN mode with  the same
period  as the  note length.   PULSE_TRAIN mode  produces a  +.5  whenever ANGLE
overflows.  We  want it to  overflow on the  first pass, so  we'll set  angle to
'3777777 (20 bits full of 1's).  The first time it gets added in with FREQUENCY,
it will overflow, producing  one pulse lasting one  pass.  If we want  more than
one random pitch per note, just decrease the period of the pulsing generator.
begin "RAND"

define ∂ = "COMMENT";
Require "LOWER.DEF[SAM,MUS]" source_file; ∂ get the source file definitions;
Require "LOWER.REL[SAM,MUS]" load_module; ∂ get the source file definitions;

string s;

real a,b;

integer  
simp,                   ∂ will contain the address of the sound producing 
                          generator;
zero,                   ∂ will be given a sum memory addr. not written into;
gen_outa,               ∂ will be the generator in WRITE_DAC mode;
outa,                   ∂ will contain a sum memory address connected to a 
                          generator in WRITE_DAC mode;
srate,                  ∂ will be the sampling rate;
frmag,                  ∂ magic number for FREQUENCY;
dfrmag,                 ∂ magic number for SWEEP;
gpmag,                  ∂ magic number for mumble;
com_chan,brk,eof,fail;  ∂ these are for SAIL i/o ;

integer loctrigger, locnoise, noise, trigger, notelength,rand;

procedure FINISH;
    begin
        GIVE(gen_outa);
        GIVE(outa);
        FLUSH; ∂ don't forget to wash your hands;
        close(com_chan);
        release(com_chan);
    end;

procedure SET_ENV_AND_DWELL(integer ug; real duration);
    begin
        own integer envstate;   
        if envstate < 0 then
            begin "ATTACK"
                BIND(ug,exponent,0);            ∂ set up initial point in ramp;
                BIND(ug,asymptote,0);           ∂ set up envelope d.c. offset;

                                    Page 89
LRNSAM                           SAIL examples                             DRAFT


                BIND(ug,rate,gpmag*duration);   ∂ set attack ramp increment;
                envstate←0;
            end "ATTACK" 
        else if envstate = 0 then
            begin "STEADYSTATE"
                BIND(ug,rate,gpmag*0);  ∂ ramp increment is zero during
                                                  steady state;
                envstate←1;
            end "STEADYSTATE"
        else begin "DECAY"
                BIND(ug,rate,gpmag*-duration);  ∂ ramp increment is negative
                                                  during decay;
                envstate←-1;
            end "DECAY";
        SET_FIELD(dwell,duration*srate);                ∂ set dwell time;
    end;

integer procedure GET_OUTN(integer chan; reference integer gen_out);
        ∂ Gets DAC output generators;
    begin
        integer sm;
        gen_out←get(id_generator);      ∂ gen_out now has addr. of 1st. 
                                          free generator;
        sm←get(id_sum_memory+last_gen_pass);
                                        ∂ sm gets addr. of 1st. free sum memory;
        bind(gen_out,fm,sm);            ∂ gen_out's fm input now reads contents
                                          of sm;
        bind(gen_out,sweep,chan);       ∂ DAC addr. is put into the sweep slot;
        bind(gen_out,mode,dac_write);   ∂ Start up generator feeding DAC;
        return(sm);                     ∂ this addr. will be used by generators 
                                          desiring output to DAC;
    end;

procedure FILOUT;
    begin
        string s;
        com_chan←getchan;
        open(com_chan,"dsk",'17,0,0,0,eof,fail);
        do begin
                outstr("output file = ");
                enter(com_chan,s←inchwl,fail);
        end until not fail;
        set_output(com_chan); ∂ this passes the channel number 
                to the box assembler;
    end;

procedure SET_CLOCK(integer nptix,nutix); 
    begin

                                    Page 90
LRNSAM                           SAIL examples                             DRAFT


        set_field(processing_ticks,nptix);
        set_field(total_ticks,nptix+nutix);
        srate←1/(0.000000195*(nptix+nutix+9));
        frmag←(1 lsh 28)/srate; ∂ frmag is the scaling 
                for FREQUENCY;
        dfrmag←frmag/srate; ∂ dfrmag is the scaling for SWEEP;
        gpmag←(1 lsh 24)/srate; ∂ gpmag is the scaling for RATE;
    end;

procedure INIT;
    begin
        INITIALIZE;                     ∂ lower level initialization;
        zero←GET(id_sum_memory+last_gen_pass);
        SET_CLOCK(64,64);       
        outa←GET_OUTN(1,gen_outa); 
        locnoise←GET(id_sum_memory+this_gen_pass);
                                        ∂ the output of the modifier must
                                          be in generator sum memory for the
                                          generator to be able to read it;

        loctrigger←GET(id_sum_memory+this_mod_pass);
                ∂ the modifier in TR_U_NOISE has to have some place to 
                  test to select its next random number.;
        FILOUT;
    end;

procedure SET_MOD(integer ug, outloc, a_read, b_read; real c0, c1,trm0, trm1);
begin
        BIND(ug,sum_memory,outloc);
        BIND(ug,a_scale,1);
        BIND(ug,b_scale,1);
        BIND(ug,term_0,trm0);
        BIND(ug,term_1,trm1);
        BIND(ug,coeff0,c0);
        BIND(ug,coeff1,c1);
        BIND(ug,a_in,a_read);
        BIND(ug,b_in,b_read);
end;

procedure SET_GEN(integer ug; real frq; integer fm_read, set_angle);
    begin

        BIND(ug,sweep,0);       ∂ set up oscilator params., this one for 
                                    sweep rate = 0;
        BIND(ug,angle,set_angle);       ∂ initial angle = 0, so we get a
                sine wave.  Had it been (2↑20)/4, it would
                    have been a cosine wave, since
                    it would start at 90 degrees;

                                    Page 91
LRNSAM                           SAIL examples                             DRAFT


        BIND(ug,fm,fm_read);    ∂ now we want FM to read from the 
                                    signal coming from the scaling 
                                    modifier;
        BIND(ug,scale,0);       ∂ scale must be set to zero when not using
                                    sum of cosines mode;
        BIND(ug,frequency,frq);
        BIND(ug,sum_memory,outa);       ∂ pass it's output to outa;
    end;

simp←GET(id_generator); ∂ gloms a generator and returns its address;
trigger←GET(id_generator); 
noise←GET(id_modifier); ∂ get modifier;

INIT;
rand←ran(0); ∂ A predeclared SAIL random number getter, set to zero always
                takes its seed as the date+TimeOfDay;
print("Note length =    ");
notelength←realscan(s←inchwl,brk);
print("Upper frequency bound =  ");
a←realscan(s←inchwl,brk);
print("Lower frequency bound =  ");
b←realscan(s←inchwl,brk);

∂ here we setup the triggering generator;
SET_GEN(trigger,frmag/notelength,zero,'3777777);
    ∂ we want to set ANGLE so it will overflow;
    ∂ in addition, we want to stop the enveloping in the "on" position so as 
        to get a non-zero pulse, as follows;
    BIND(trigger,exponent,1);   ∂ set to maximum to pass oscillator pulse as is;
    BIND(trigger,asymptote,0);  ∂ no dc offset else we'd get constant new notes;
    BIND(trigger,rate,0);       ∂ set attack ramp increment to zero so it
                                  will stay at maximum;


SET_MOD(noise, locnoise,loctrigger,zero,frmag*((a-b)/2),1, 0, rand); 
        ∂ here's where we stick the first term of the frequency scaling;

SET_GEN(simp,frmag*((a-b)/2), locnoise,0);      
        ∂ here's where we stick the second term of the frequency scaling;

SET_ENV_AND_DWELL(simp,notelength/3);           ∂ now we get the attack, set
        here arbitrarily to be 1/3 of the note's length;

∂ here ends the initialization.  All parameters must be in place before
        any run modes are passed;
BIND(trigger,mode,pulse_train);
BIND(noise,mode,tr_u_noise);
BIND(simp,mode,a_running+lplusq+sine);

                                    Page 92
LRNSAM                           SAIL examples                             DRAFT



SET_ENV_AND_DWELL(simp,notelength/3);           ∂ now the steady state;
SET_ENV_AND_DWELL(simp,notelength/3);           ∂ now we get the decay;

GIVE(simp);
GIVE(noise);
GIVE(trigger);
FINISH;
end "RAND";



4-3. Delay line example

The following is  supplied as a  terse example of using  a delay line  for table
lookups.  It just  shows  how to  connect  delay memory  to  a delay  line  to a
modifier, and how to  fill the delay memory.  Left  out are all the  settings of
the modifier parameters and read-write addresses to sum memory and the interface
to either the DACs or a file.
begin "delay"

require "lower.def" source_file;
require "lower.rel" load_module;
integer delay_port,mod;
define ∂ = "comment ";
define len = "10";
define loc = "1033"; ∂ some random place in delay memory;
preload_with 0,1,2,3,4,5,6,7,8,9;
safe integer array table[0:len-1];

load_delay(table,loc,len); ∂ stuff array TABLE into someplace in delay memory;
mod ← get(id_modifier);
delay_port ← get(id_delay);
bind(delay_port,mode,table_lookup); ∂ set to table lookup mode;
bind(delay_port,base_address,loc); ∂ this delay starts at loc;
bind(delay_port,delay_length,len); ∂ len lookups;
bind(delay_port,index,len/2); ∂ set index to some location or other;
bind(mod,invoke_delay_unit,delay_port); ∂ delay_port is bound to mod;

∂ we assume great things happen here;

∂ now lets change the function of the delay unit to delay line mode;
bind(delay_port,mode,delayline); ∂ We can now use this as a delay line;

∂ More great things happen here;

give(delay_port); ∂ OK, give it back!;
give(mod);

                                    Page 93
LRNSAM                           SAIL examples                             DRAFT


end "delay";



4-4. Writing your own procedures

The following is an example of how you could write your own procedures to handle
box setup.  This routine gets N consecutively numbered generators.  It  looks up
array UNIT_GENERATORS which contains a location for all the elements in  the box
and whether they are in use  or not.  It requires a global variable  NPTIX which
would contain the number of  processing ticks available on this pass,  and hence
the number of processing elements that can run.  It also needs to know what kind
of  processing  element  you   want,  specifying  it  by   saying  ID_GENERATOR,
ID_MODIFIER , ID_DELAY or ID_SUM_MEMORY which are defined in LOWER.DEF to  be 0,
1, 2 and 3  respectively.  The routine returns the  number of the first  unit in
the sequence. If there are not N units in a row, it returns -1.

integer procedure GET_N_UNITS(integer TYPE, NLOCS);
    begin "GET_N"
        integer I,J,BASE,LIMIT,NEED_N_TIX;
        define ⊂ = "COMMENT";
        boolean GOTIT;

        ⊂ BASE contains the base address of the type
                of element in UNIT_GENERATORS;
        base← case TYPE of(0,256,384,640);
        ⊂ now figure out how many processing ticks the desired
                number of processing elements would take;
        case type of
        begin
                [0] need_n_tix←nptix; ⊂ generators take only one
                        processing tick;
                [1] need_n_tix←nptix/2; ⊂ it takes two processing
                        ticks per modifier;
                [2] need_n_tix←nptix; ⊂ delay memory takes no
                        processing ticks;
                [3] need_n_tix←(nptix-6)/4  ⊂ this is the formula
                        for available delay cycles;
        end;

        ⊂ LIMIT has maximum processing ticks for all of a
                type of element to be running;
        limit← case type of(256,256,0,134);
        if nlocs > need_n_tix then
        begin
                outstr("You are too greedy");
                return(0);
        end;

                                    Page 94
LRNSAM                           SAIL examples                             DRAFT


        gotit←false;
        for i←0 step 1 until NPTIX-NLOCS do
        begin "ISTART"
                if ¬UNIT_GENERATORS[i+base] then
                begin "CHKIT"
                    for j←i+1 step 1 until i+NLOCS-1 do
                        if UNIT_GENERATORS[j+base] then
                        begin "NOGOOD"
                            i←j;
                            continue "ISTART";
                        end "NOGOOD";
                    gotit←true;
                    done "ISTART";
                end "CHKIT";
        end "ISTART";
        if ¬gotit then return(-1);
        for j←i step 1 until i+NLOCS-1 do
            UNIT_GENERATORS[j+base]←true;
        return(i);
    end "GET_N";





























                                    Page 95
LRNSAM                                                                     DRAFT


                                   Appendix 5
                                   ________ _

                        Generator field and mode tables
                        _________ _____ ___ ____ ______





5-1. Generator parameters
ID    SIZE  DEFINITION FUNCTION
--------------------------------------------------------------------------------
GO    20    SWEEP      oscillator frequency sweep rate
GJ    28    FREQUENCY  oscillator frequency
GK    20    ANGLE      oscillator angle
GN    11    NCOSINES   number of cosines to be summed
GM    4     SCALE      binary scale of sine or sum of cosines
GP    20    RATE       envelope rate of change
GQ    24    EXPONENT   envelope current value
GL    12    ASYMPTOTE  asymptote (DC offset of the envelope)
GSUM  6     SUM_MEMORY sum memory location into which
                       output is added
GFM   7     FM         sum memory address from which frequency
                       modulation data is taken
GMODE 10    MODE       generator mode, including run mode and
                       type of oscillator and enveolpe processing
                       (see list of modes on page 22).
            OSC_MODE   sets oscillator field of GMODE without
                       altering run mode or envelope mode.
            ENVELOPE   sets envelope field of GMODE without
                       altering run mode or oscillator mode.



5-2. Generator run modes
Definition             Osc. run? Envelope run?   Add to sum?
--------------------------------------------------------------------------------
G_INACTIVE             no        no              no
G_PAUSE                no        no              no
G_WAIT                 yes       no              no
x_RUNNING:
   A_RUNNING           yes       yes             yes
   B_RUNNING           yes       yes             yes
   C_RUNNING           yes       yes             yes
DATA_READ              no        yes             yes
DATA_WRITE             no        no              no
DAC_WRITE              no        no              no



                                    Page 96
LRNSAM                  Generator field and mode tables                    DRAFT


5-3. Oscillator modes

--------------------------------------------------------------------------------
   SAWTOOTH        sawtooth wave
   SQUARE          square wave
   SUM_OF_COSINES  sum of cosines
   PULSE_TRAIN     pulse train
   SINE            sine(ANGLE),normal sine wave
                   mode (with fm).
   SIN_FM          sine(FREQUENCY + FM)
                   special sine table lookup mode.



5-4. Envelope modes
Definition Function
--------------------------------------------------------------------------------
LPLUSQ     Add EXPONENT (GQ) to the offset constant
           in ASYMPTOTE (GL).
LMINUSQ    Subtract EXPONENT (GQ) from offset constant
           in ASYMPTOTE(GL)
                                  (-EXPONENT (GQ))
LEXPLUS    Add ASYMPTOTE (GL) to 2                 and scale.
                                       (-EXPONENT (GQ))
LEXMINUS   Subtract ASYMPTOTE (GL) to 2                 and scale.
























                                    Page 97
LRNSAM                                                                     DRAFT


                                   Appendix 6
                                   ________ _

                         Modifier field and mode tables
                         ________ _____ ___ ____ ______





6-1. Modifier parameters
Id    Size Definition          Function
--------------------------------------------------------------------------------
M0    30   COEFF0              coefficient
M1    30   COEFF1              other coefficient
L0    20   TERM_0              running term
L1    20   TERM_1              other running term
MIN   8    A_IN                read address in sum memory
MRM   8    B_IN                other read address
MSUM  7    SUM_MEMORY          write address in
                               sum memory
           ADD_SUM_MEMORY      same as SUM_MEMORY.
           REPLACE_SUM_MEMORY  also has write addr.
                               but result replaces sum mem. value
MMODE 9    MODE                run mode of modifier
                               and scale factor for the multiplies
              A_SCALE          scales COEFF1.
              B_SCALE          scales COEFF0.























                                    Page 98
LRNSAM                   Modifier field and mode tables                    DRAFT


6-2. Modifier procedures
Definition Function
--------------------------------------------------------------------------------
M_INACTIVE inactive.
MIXING      fixed binary point result of COEFF_0*A_IN + COEFF_1*B_IN.

INT_MIXING Integer result of COEFF_0*A_IN + COEFF_1*B_IN.

LATCH      Result ← Term_1. If B_IN*COEFF_0 ≠ 0 then TERM_1←A_IN.

SIGNUM     If A_IN*COEFF0 < B_IN*COEFF1 then result ← -1 else if
           A_IN*COEFF0 = B_IN*COEFF1 then result ← 0 else if
           A_IN*COEFF0 > B_IN*COEFF1 then result ← 1.

ZERO_CROSSING_PULSER
           If (B_IN*COEFF0)*(L1*COEFF1) ≤ 0 then result ← - 1
           else result ← 0. L1 ← B_IN*COEFF0.

MINIMUM    The lesser of A_IN*COEFF0 or B_IN*COEFF1 is returned.

MAXIMUM    The greater of A_IN*COEFF0 or B_IN*COEFF1 is returned.

AMPLITUDE_MODULATION
           Result ← L1*COEFF1.  L1 ← A_IN*((B_IN+1)/2).

FOUR_QUAD_MULTIPLY
           Result ← TERM_1*COEFF1.  TERM_1 ← A_IN*B_IN.

U_NOISE    TERM_1 ← Result ← COEFF0*TERM_1 + TERM_0.

TR_U_NOISE Result ← COEFF0*TERM_1 + TERM_0.
           If B_IN*COEFF1 ≠ 0 then TERM_1←result.

THRESHOLD  If COEFF0*A_IN + TERM_0 < 0 then result ← 0, else
           if ≥ 0 then result ← COEFF1*B_IN.

FILTERING:
           ONE_POLE
           ONE_ZERO
           TWO_0POLES COEFF0 variable
           TWO_1POLES COEFF1 variable
           TWO_0ZEROS COEFF0 variable
           TWO_1ZEROS coeff1 variable






                                    Page 99
LRNSAM                                                                     DRAFT


                                   Appendix 7
                                   ________ _

                        Delay unit field and mode tables
                        _____ ____ _____ ___ ____ ______


Fields:

Id     Definition    Function
--------------------------------------------------------------------------------
P      MODE          Delay unit mode
Z      DELAY_LENGTH  Delay line unit length
       SCALE          or table address scale factor
Y      INDEX         Running index of the delay array
X      BASE_ADDRESS  Base address of delay array

Modes:
       D_INACTIVE    dead
       DELAYLINE
       TABLE_LOOKUP
       ROUNDED_TABLE_LOOKUP



7-1. Delay units: timing

Delay units have their own formula: Delay memory cycles available per pass =

(<#_of_processing_ticks> - 6) / 4.                                       Equ. 18

(If  you are  loading delay  memory  while doing  processing, i.e.,  if  you are
changing delay memory arrays from the main computer during execution of samples,
then the number of delay cycles is this number less however many cycles the main
computer may  take to  load the  array.  Each  delay memory  cycle used  to load
arrays in this  fashion will load  one word.  This  method probably will  not be
used much.  The typical usage will be to load the arrays before cranking  up the
box, so that the sound processing is not in competition with computer I/O.)













                                    Page 100
LRNSAM                                                                     DRAFT


                                   Appendix 8
                                   ________ _

                                 Reserved Words
                                 ________ _____

a_in                                     dac_write
a_running                                data_read
a_scale                                  data_write
adc                                      decode
add_sum_memory                           delay
all_sum_memory                           delay_length
angle                                    delayline
asymptote                                delays
b_in                                     diagnostic_address
b_running                                dis_ticks
b_scale                                  disk_in
base_address                             disk_out
bias_delay                               dmio
bias_end                                 doinp
bias_generator                           dooutp
bias_modifier                            drio
bias_sum_memory                          dwell
bind                                     dx
bind_field                               dx_load
c_running                                ena_ticks
c_start                                  envelope
c_stop                                   envlsb
c_tick                                   envmask
clear_passes_dwell                       exp_just(x)
coeff0                                   exponent
coeff1                                   flush
coni_a                                   fm
coni_b                                   fn_maximum
coni_d                                   fn_minimum
coniab                                   fnlsb
cono_a                                   fnmask
cono_b                                   four_quad_multiply
control_mode                             freq_just(x)
cos_fm                                   frequency
cosine                                   full_word
d_data(x)                                function
d_inactive                               g_data(x)
d_number(x)                              g_inactive
d_op(x)                                  g_number(x)
d_whole_data(x)                          g_op(x)
dac                                      g_pause




                                    Page 101
LRNSAM                           Reserved Words                            DRAFT






g_wait                                   misc_stop(x)
gen_last_pass_sum_memory                 misc_wait_clear(x)
gen_this_pass_sum_memory                 mixing
generators                               mod_last_pass_sum_memory
get                                      mod_this_pass_sum_memory
give                                     mode
id_delay                                 modifiers
id_generator                             ncosines
id_maximum                               no_asymptote(x)
id_modifier                              no_fm(x)
id_sum_memory                            no_gmode(x)
index                                    no_gsum(x)
inhibit_processing_ticks                 no_in(x)
initialize                               no_l(x)
int_mixing                               no_m(x)
k_clear(x)                               no_mmode(x)
l0_clear(x)                              no_msum(x)
l1_clear(x)                              no_n(x)
latch                                    no_ncosines(x)
left_justified                           no_rm(x)
lexpminus                                no_scale(x)
lexpplus                                 non_optimize
lminusq                                  notwopoles
load_delay                               notwozeroes
lounge                                   o_angle
lplusq                                   o_asymptote
m_data(x)                                o_base
m_inactive                               o_delay
m_just(x)                                o_dmode
m_number(x)                              o_exponent
m_op(x)                                  o_fm
m_select(x)                              o_frequency
max_envelope                             o_gmode
max_oscillator                           o_gsum
maximum                                  o_in
min_envelope                             o_index
min_oscillator                           o_l
minimum                                  o_l_0
misc_data(x)                             o_l_1
misc_op(x)                               o_m
misc_pause_clear(x)                      o_m_0




                                    Page 102
LRNSAM                           Reserved Words                            DRAFT






o_m_1                                    osclsb
o_mmode                                  oscmask
o_msum                                   p_a_scale(x)
o_ncosines                               p_angle(x)
o_rate                                   p_asymptote(x)
o_rm                                     p_b_scale(x)
o_scale                                  p_cause(x)
o_size                                   p_control(x)
o_sweep                                  p_d_mode(x)
one_pole                                 p_d_scale(x)
one_zero                                 p_drb(x)
op_angle                                 p_exponent(x)
op_asymptote                             p_fm(x)
op_base                                  p_frequency(x)
op_delay                                 p_g_envelope(x)
op_dmode                                 p_g_osc_mode(x)
op_exponent                              p_g_run_mode(x)
op_fm                                    p_gmode(x)
op_frequency                             p_gsum(x)
op_gmode                                 p_in(x)
op_gsum                                  p_int(x)
op_in                                    p_l_0(x)
op_index                                 p_l_1(x)
op_l                                     p_l(x)
op_l_0                                   p_m_0(x)
op_l_1                                   p_m_1(x)
op_m                                     p_m_function(x)
op_m_0                                   p_m(x)
op_m_1                                   p_mmode(x)
op_mmode                                 p_mr(x)
op_msum                                  p_msum(x)
op_ncosines                              p_ncosines(x)
op_rate                                  p_pia(x)
op_rm                                    p_rate(x)
op_scale                                 p_re(x)
op_size                                  p_rm(x)
op_sweep                                 p_rtc(x)
op_ticks                                 p_scale(x)
op_timer                                 p_spt(x)
optimize                                 p_sweep(x)
osc_mode                                 packing_mode




                                    Page 103
LRNSAM                           Reserved Words                            DRAFT






pass                                     time_data(x)
pass_clear                               time_op(x)
pass_set                                 tix_processing
pause_clear                              tix_total
permit_processing_ticks                  total_ticks
PIA                                      tr_u_noise
processing_ticks                         ttl_load
pulse_train                              ttla
qflush                                   ttlb
qlens                                    two_0poles
rate                                     two_0zeroes
relative                                 two_1poles
replace_sum_memory                       two_1zeroes
reset                                    u_noise
reset_tick_counter                       unit_generators
right_justified                          update_tick
round_table_lookup                       wait_clear
run_mode                                 zero_crossing_pulser
runlsb
runmask
sawtooth
scale
set_channel
set_field
set_mode
set_output
set_passes
set_procedure
signum
size_buffer
size_commands
square
stop
sum_memory
sum_of_cosines
sweep
table_lookup
term_0
term_1
tick_data(x)
tick_op(x)




                                    Page 104
LRNSAM                                                                     DRAFT


                                   Appendix 9
                                   ________ _

                                 Error messages
                                 _____ ________


Bind: Delay port used for sum memory
Bind: Identifier out of range
Bind: Invalid name for identifier type
Bind: Invalid sum memory location
Bind: name out of range
Get: Identifier type out of range
Give: identifier out of range
Set_channel: Zero channel
Set_field: Field index out of range
Set_mode: Mode index out of range
Warning: Extraneous bits on in generator bind
Warning: Extraneous bits on in modifier coefficient bind
Warning: No output channels!































                                    Page 105
LRNSAM                               Index                                 DRAFT


                                   I N D E X

                        (References are to Page numbers)


A_IN                            43, 45    COSINE                              31
A_RUNNING                           23    CPU                                 13
A_SCALE                             43
ADC                                 66    DAC                             36, 66
ADD_SUM_MEMORY                      44    DAC path                            14
Addressing sum memory               17    DAC_WRITE                           24
Amplitude modulation                48    data underrun                        8
AMPLITUDE_MODULATION                48    DECODE                              68
ANGLE                       27, 37, 39    Definition                      20, 34
Arithmetic overflow     23, 24, 46, 48    Definitions                          2
array all_sum_memory                73    delay line                          10
array delays                        73    Delay line example                  93
array gen_sum_memory                73    Delay line mode                     61
array gen_this_pass_sum_memory      73    delay memory             2, 10, 11, 61
array generators                    73    Delay unit                       2, 11
array mod_sum_memory                73    Delay unit field and mode tables   100
array modifiers                     73    Delay Units                 61, 62, 63
array unit_generators               73    Delay units: timing                100
ASYMPTOTE                   27, 32, 38    DELAYLINE                           61
                                          digital filtering theory            54
B_IN                            43, 45    direct memory access                13
B_RUNNING                           23    disk                                66
B_SCALE                             43    disk bandwidth                       8
band limiting                       31    DMA                             13, 24
band-limited pulse train            30    Duration scaling                    40
bandwidth                           55    DWELL                               14
base address                        68
Basic number theory     79, 80, 81, 82    envelope                        10, 19
BIND                                65    Envelope modes                  34, 35
BIND_FIELD                          70    Envelope processing             32, 33
block structure                     75    Error messages                     105
                                          exiting                             72
C_RUNNING                           24    EXPONENT                    27, 32, 38
Chaining processing elements        16
clear all pause bits                22    Filtering algorithms52, 53, 54, 55,
clear all wait bits                 23                            56, 57, 58, 59
COEFF0                              43    fixed binary point notation         45
COEFF1                              43    flow of data                13, 14, 15
Coefficient terms                   43    FLUSH                               72
command buffer                      71    FM                          24, 27, 38
Command path                        13    foldover                            31
command underrun                15, 71    formant filter                      55
COS_FM                          30, 37    Four quadrant multiply              48

                                    Page 106
LRNSAM                               Index                                 DRAFT







FOUR_QUAD_MULTIPLY                  48    Latch                               46
Four-quadrant-multiply              48    left-shifting                       43
fractional notation                 45    LEXMINUS                            35
Free mode                           23    LEXPLUS                             34
FREQUENCY                   27, 37, 39    LMINUSQ                             34
frequency response                  54    LOAD_DELAY                          71
Frequency scaling                   39    low-pass filter                     54
                                          LOWER.DEF                           64
G_PAUSE                             22    LOWER.REL                           64
G_WAIT                          23, 24    LPLUSQ                              34
generator                        2, 77    LSH                                 43
Generator field and mode tables 96, 97
Generator parameters                20    M_INACTIVE                          45
Generator run modes     22, 23, 24, 25    MAG                                 39
Generator tick time                  5    Magic numbers               39, 40, 41
Generators              10, 19, 20, 21    Maximum                             47
GET                                 64    Minimum                             47
GFM                                 38    Mixing                              45
GIVE                                65    MODE                                43
GJ                                  37    modifier                         2, 77
GK                                  37    Modifier field and mode tables  98, 99
GL                                  38    Modifier parameters                 42
GM                                  37    Modifier procedures                 99
GN                                  37    Modifier scaling                    43
GO                                  36    Modifier tick time                   5
GP                              32, 38    Modifiers           10, 42, 43, 44, 61
GQ                              32, 38
GSUM                            21, 38    NCOSINES                            37
                                          Normalizing                         38
high-pass filter                    55    Numbering Processing elements        9

I/O path                            14    One pole                        52, 54
INACTIVE                        22, 45    One zero                        52, 55
Initialization          37, 44, 47, 62    oscillator                      10, 19
INITIALIZE                          71    Oscillator modes            29, 30, 31
INT_MIXING                          46
Integer mixing                      46    pass                  2, 7, 73, 76, 78
Integer notation                    46    pass period                          8
Interrupt                           14    pipelining                      75, 76
Invoke delay unit                   50    processing degradation               8
INVOKE_DELAY_UNIT                   50    processing element                   2
                                          Processing element arrays           72
last pass                           16    processing elements         10, 11, 77

                                    Page 107
LRNSAM                               Index                                 DRAFT







Processing speed               7, 8, 9    sum memory                   2, 10, 11
Processing ticks            2, 3, 4, 8    SUM_MEMORY              24, 33, 38, 44
pulse train                         30    SUM_OF_COSINES              30, 31, 37
PULSE_TRAIN                     30, 31    SWEEP                       24, 27, 36
                                          SWEEP scaling                       40
RATE                        27, 32, 38
Read path                           14    table lookup                        71
Read terms                          43    table lookup memory                 10
READ_DATA                           24    Table lookup mode               61, 93
Real time                         7, 8    TABLE_LOOKUP                        62
refreshing                          27    TERM_0                          42, 48
RELATIVE                            68    TERM_1                          42, 46
REPLACE_SUM_MEMORY                  44    this pass                           16
Reserved Words      101, 102, 103, 104    Threshold                           50
Resonant filter scaling             55    tick                             2, 78
resonant frequency                  55    Tick counters                       73
resonator                           55    Tick time requirements            5, 6
Ring modulation                     48    Time division multiplexing      77, 78
ROUND_TABLE_LOOKUP                  62    TR_U_NOISE                          49
Running terms                       42    Trigger mode                        24
                                          Triggered uniform noise             49
SAIL                            20, 64    Two poles                       53, 55
SAIL examples  83, 84, 85, 86, 87, 88,    Two poles COEFF0 variable           53
            89, 90, 91, 92, 93, 94, 95    Two poles COEFF1 variable           53
sample                            1, 2    two quadrant multiply               48
sample period                        8    Two zeros                           53
sampling rate                        1    Two zeros COEFF0 variable           54
SAWTOOTH                        29, 31    Two zeros COEFF1 variable           54
SCALE                           37, 46
Scaling terms                       43    U_NOISE                             48
SET_CHANNEL                         66    Uniform noise                       48
SET_FIELD                           69    Update ticks                2, 3, 4, 8
SET_MODE                            68    update_tick                         73
SET_OUTPUT                      24, 66
SET_PROCEDURE                       67    Write path                          14
Signum                              47    WRITE_DAC                   36, 37, 66
SINE                                29    WRITE_DATA                      24, 37
Size of command buffer              71    Writing your own procedures         94
source file                         64
Speed of processing                  7    x_RUNNING                           23
SQUARE                          30, 31
Sticky mode                     23, 32    ZERO_CROSSING_PULSER                47
Stream processing                   14    Zero-crossing pulser                47

                                    Page 108